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/.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 294fc2e50f50b..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 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 cadb920fa022f..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@v4 + - 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@v4 + - uses: actions/cache@v5 name: Configure pip caching with: path: ~/.cache/pip 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/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 b7bf6920a5c7c..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,9 +103,9 @@ 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 @@ -108,16 +113,17 @@ class CCDBManagerInstance /// 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 metaData = MD()); + 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; } @@ -129,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 @@ -228,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) { @@ -250,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++; @@ -305,7 +332,6 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) } else { cached.cacheValidUntil = -1; } - mHeaders.clear(); mMetaData.clear(); if (!ptr) { if (mFatalWhenNull) { @@ -316,6 +342,13 @@ 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; } @@ -328,7 +361,7 @@ T* CCDBManagerInstance::getForRun(std::string const& path, int runNumber, bool s } template -T* CCDBManagerInstance::getSpecificForRun(std::string const& path, int runNumber, MD metaData) +T* CCDBManagerInstance::getSpecificForRun(std::string const& path, int runNumber, MD const& metaData) { auto [start, stop] = getRunDuration(runNumber, mFatalWhenNull); if (start < 0 || stop < 0) { @@ -367,4 +400,4 @@ class BasicCCDBManager : public CCDBManagerInstance } // namespace o2::ccdb -#endif //O2_BASICCCDBMANAGER_H +#endif // O2_BASICCCDBMANAGER_H diff --git a/CCDB/include/CCDB/CcdbApi.h b/CCDB/include/CCDB/CcdbApi.h index 1308742b57fd0..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. @@ -556,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; @@ -576,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; diff --git a/CCDB/src/BasicCCDBManager.cxx b/CCDB/src/BasicCCDBManager.cxx index bcf88554578c1..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 diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index bb2b69e84c4f7..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://"; @@ -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); } @@ -755,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); @@ -830,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 @@ -878,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; } @@ -885,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); } @@ -977,7 +989,7 @@ bool CcdbApi::initTGrid() const 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; @@ -1016,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 @@ -1164,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); @@ -1218,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()); } @@ -1230,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); @@ -1290,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(); @@ -1436,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) { @@ -1632,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 @@ -1728,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); 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/CODEOWNERS b/CODEOWNERS index 5337622522bbb..26021d458ad76 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -28,13 +28,13 @@ /DataFormats/Detectors/Common @shahor02 /DataFormats/Detectors/CPV @peressounko @kharlov /DataFormats/Detectors/CTP @lietava -/DataFormats/Detectors/EMCAL @mfasDa @jokonig +/DataFormats/Detectors/EMCAL @nstrangm @jokonig /DataFormats/Detectors/FIT @jotwinow @afurs @andreasmolander @sahilupadhyaya92 -/DataFormats/Detectors/FOCAL @maxrauch @mfasDa @iarsene @matthiasrichter +/DataFormats/Detectors/FOCAL @maxrauch @iarsene @matthiasrichter /DataFormats/Detectors/GlobalTracking @shahor02 /DataFormats/Detectors/GlobalTrackingWorkflow @shahor02 /DataFormats/Detectors/HMPID @gvolpe79 -/DataFormats/Detectors/ITSMFT @fprino @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 @@ -58,9 +58,9 @@ /Detectors/Base @sawenzel @shahor02 /Detectors/Calibration @chiarazampolli @shahor02 /Detectors/CPV @peressounko @kharlov -/Detectors/EMCAL @mfasDa @jokonig +/Detectors/EMCAL @nstrangm @jokonig /Detectors/FIT @jotwinow @afurs @andreasmolander @sahilupadhyaya92 -/Detectors/FOCAL @maxrauch @mfasDa @iarsene @matthiasrichter +/Detectors/FOCAL @maxrauch @iarsene @matthiasrichter /Detectors/Geometry @sawenzel @shahor02 /Detectors/GlobalTracking @shahor02 /Detectors/GlobalTrackingWorkflow @shahor02 diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index da34230662ef6..46aeff98d6033 100644 --- a/Common/Constants/include/CommonConstants/PhysicsConstants.h +++ b/Common/Constants/include/CommonConstants/PhysicsConstants.h @@ -31,6 +31,9 @@ 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, @@ -88,10 +91,14 @@ enum Pdg { kHyperHydrogen4 = 1010010040, kHyperHelium4 = 1010020040, kHyperHelium5 = 1010020050, - kHyperHelium4Sigma = 1110020040 + 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; @@ -150,6 +157,7 @@ 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; diff --git a/Common/Constants/include/CommonConstants/make_pdg_header.py b/Common/Constants/include/CommonConstants/make_pdg_header.py index b94cc34599d5e..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,6 +89,9 @@ class PdgROOT(Enum): # Enum of additional particles class Pdg(Enum): + kEta = 221 + kOmega = 223 + kEtaPrime = 331 kB0 = 511 kB0Bar = -511 kBPlus = 521 @@ -144,9 +150,10 @@ class Pdg(Enum): kHyperHelium4 = 1010020040 kHyperHelium5 = 1010020050 kHyperHelium4Sigma = 1110020040 + kLambda1520_Py = 102134 # PYTHIA code different from PDG -dbPdg = ROOT.o2.O2DatabasePDG +dbPdg = o2.O2DatabasePDG def mass(code): @@ -156,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 5c3a93aa7fa74..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) diff --git a/Common/DCAFitter/GPU/cuda/CMakeLists.txt b/Common/DCAFitter/GPU/cuda/CMakeLists.txt index ddc1d09445d7f..6b89207279fe0 100644 --- a/Common/DCAFitter/GPU/cuda/CMakeLists.txt +++ b/Common/DCAFitter/GPU/cuda/CMakeLists.txt @@ -22,14 +22,14 @@ o2_add_library(DCAFitterCUDA 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}) \ No newline at end of file +#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/hip/CMakeLists.txt b/Common/DCAFitter/GPU/hip/CMakeLists.txt index f62759bb6ea2c..5e7821a0b8946 100644 --- a/Common/DCAFitter/GPU/hip/CMakeLists.txt +++ b/Common/DCAFitter/GPU/hip/CMakeLists.txt @@ -21,15 +21,15 @@ o2_add_hipified_library(DCAFitterHIP 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}) \ No newline at end of file +#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/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index df732bd4bde63..1adf7a9ae7329 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -17,7 +17,7 @@ #ifndef _ALICEO2_DCA_FITTERN_ #define _ALICEO2_DCA_FITTERN_ -#include "DCAFitter/HelixHelper.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include "DetectorsBase/Propagator.h" #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/Track.h" 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/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/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/include/ML/3rdparty/GPUORTFloat16.h b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h index 3bf2f465b2a35..75e146d872cd1 100644 --- a/Common/ML/include/ML/3rdparty/GPUORTFloat16.h +++ b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h @@ -568,9 +568,11 @@ GPUdi() uint16_t BFloat16Impl::ToUint16Impl(float v) noexcept 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); diff --git a/Common/ML/include/ML/OrtInterface.h b/Common/ML/include/ML/OrtInterface.h index 0c498e33d2e2c..987ce8fb4d6dd 100644 --- a/Common/ML/include/ML/OrtInterface.h +++ b/Common/ML/include/ML/OrtInterface.h @@ -51,6 +51,7 @@ class OrtModel 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(); @@ -116,7 +117,7 @@ class OrtModel int32_t mInputsTotal = 0, mOutputsTotal = 0; // Total number of inputs and outputs // Environment settings - bool mInitialized = false; + 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; diff --git a/Common/ML/src/OrtInterface.cxx b/Common/ML/src/OrtInterface.cxx index 1cd9913efb6aa..8f88ab18dacbd 100644 --- a/Common/ML/src/OrtInterface.cxx +++ b/Common/ML/src/OrtInterface.cxx @@ -54,7 +54,7 @@ void OrtModel::initOptions(std::unordered_map optionsM // Load from options map if (!optionsMap.contains("model-path")) { - LOG(fatal) << "(ORT) Model path cannot be empty!"; + LOG(fatal) << "(ORT) Model path must be contained in options map!"; } if (!optionsMap["model-path"].empty()) { @@ -68,6 +68,7 @@ void OrtModel::initOptions(std::unordered_map optionsM 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); @@ -99,6 +100,10 @@ void OrtModel::initOptions(std::unordered_map optionsM (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)); @@ -133,6 +138,24 @@ void OrtModel::initEnvironment() (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) { diff --git a/GPU/GPUTracking/ITS/GPUITSTrack.h b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h similarity index 50% rename from GPU/GPUTracking/ITS/GPUITSTrack.h rename to Common/MathUtils/include/MathUtils/BetheBlochAleph.h index 5063985692a43..bd72faffb0503 100644 --- a/GPU/GPUTracking/ITS/GPUITSTrack.h +++ b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h @@ -9,24 +9,27 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUITSTrack.h -/// \author David Rohr, Maximiliano Puccio +#ifndef AliceO2_COMMON_BETHEBLOCH_H_ +#define AliceO2_COMMON_BETHEBLOCH_H_ -#ifndef GPUITSTRACK_H -#define GPUITSTRACK_H +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" -#include "GPUTPCGMMergerTypes.h" -#include "GPUTPCGMTrackParam.h" - -namespace o2::gpu +namespace o2::common { -class GPUITSTrack : public GPUTPCGMTrackParam + +template +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) { - public: - gputpcgmmergertypes::GPUTPCOuterParam mOuterParam; - float mAlpha; - int32_t mClusters[7]; -}; -} // namespace o2::gpu + 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/SimConfig/include/SimConfig/G4Params.h b/Common/SimConfig/include/SimConfig/G4Params.h index fd36ae046d520..aa8aa05263c0a 100644 --- a/Common/SimConfig/include/SimConfig/G4Params.h +++ b/Common/SimConfig/include/SimConfig/G4Params.h @@ -33,6 +33,13 @@ enum class EG4Physics { 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_EMV_optical; // default physics mode with which to configure G4 @@ -40,6 +47,8 @@ struct G4Params : public o2::conf::ConfigurableParamHelper { 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/SimParams.h b/Common/SimConfig/include/SimConfig/SimParams.h index cf3ee2b01cf2e..b5f975d1b0c6e 100644 --- a/Common/SimConfig/include/SimConfig/SimParams.h +++ b/Common/SimConfig/include/SimConfig/SimParams.h @@ -44,7 +44,11 @@ struct SimCutParams : public o2::conf::ConfigurableParamHelper { 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; // global factor that scales all material densities for systematic studies - std::string localDensityFactor; // Expected format: "SimMaterialParams.localDensityFactor=:,:,..." + // 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/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 9407a3c556179..15879687872d5 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -98,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]); } } @@ -112,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]); } } @@ -130,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)); } @@ -149,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)); } } @@ -197,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") { 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/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 18f2aa7c1b6ed..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 @@ -87,7 +96,16 @@ o2_add_test(EnumFlags 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/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 index fcd7d2d9e5e26..e7481c903e666 100644 --- a/Common/Utils/include/CommonUtils/EnumFlags.h +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -54,9 +54,12 @@ concept EnumFlagHelper = requires { // 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 { @@ -107,7 +110,7 @@ struct FlagsHelper final { 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 'localation' contains an enum. + // Checks if a given 'location' contains an enum. template static constexpr bool isValid() noexcept { @@ -127,14 +130,14 @@ struct FlagsHelper final { // check if this is an anonymous enum return true; } - return false; -#else +#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 { - return false; } +#else +#error Unsupported compiler #endif + return false; } // Extract which values are present in the enum by checking all values in @@ -144,8 +147,8 @@ struct FlagsHelper final { { 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, "Requiring non-empty enum!"); - static_assert(count <= MaxUnderScan, "Underlying type of enum has less digits than given expected!"); + 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]) { @@ -160,8 +163,17 @@ struct FlagsHelper final { 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 constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous - static constexpr auto MaxRep{((1 << (Max_u_v - Min_u_v + 1)) - 1) << Min_u_v}; // largest representable value + 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() @@ -172,7 +184,7 @@ struct FlagsHelper final { } if constexpr (tpeek_v[tp + getSpec().size()] == getSpec()) { #if defined __clang__ - if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { + if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { return {}; } #endif @@ -214,7 +226,7 @@ struct FlagsHelper final { template static constexpr auto getNameValue{getName()}; - template + template static constexpr auto getNames(std::index_sequence /*unused*/) { if constexpr (with_scope) { @@ -247,8 +259,8 @@ struct FlagsHelper final { static constexpr std::optional fromString(std::string_view str) noexcept { - for (std::size_t i{0}; i < count(); ++i) { - if (Names[i] == str || NamesScoped[i] == str) { + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], str) || isIEqual(NamesScoped[i], str)) { return Values[i]; } } @@ -267,7 +279,7 @@ struct FlagsHelper final { return toLower(a) == toLower(b); } - // Case-insensitive comparision for string_view. + // Case-insensitive comparison for string_view. static constexpr bool isIEqual(std::string_view s1, std::string_view s2) noexcept { if (s1.size() != s2.size()) { @@ -284,7 +296,7 @@ struct FlagsHelper final { static constexpr std::string_view None{"none"}; static constexpr bool hasNone() noexcept { - // check that enum does not contain memeber named 'none' + // check that enum does not contain member named 'none' for (size_t i{0}; i < count(); ++i) { if (isIEqual(Names[i], None)) { return true; @@ -296,7 +308,7 @@ struct FlagsHelper final { static constexpr std::string_view All{"all"}; static constexpr bool hasAll() noexcept { - // check that enum does not contain memeber named 'all' + // check that enum does not contain member named 'all' for (size_t i{0}; i < count(); ++i) { if (isIEqual(Names[i], All)) { return true; @@ -322,9 +334,9 @@ concept EnumFlag = requires { }; /** - * \brief Classs to aggregate and manage enum-based on-off flags. + * \brief Class to aggregate and manage enum-based on-off flags. * - * This class manages flags as bits in the underlying type of an enum, allowing + * 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. @@ -345,6 +357,7 @@ concept EnumFlag = requires { template class EnumFlags { + static constexpr int DefaultBase{2}; using H = details::enum_flags::FlagsHelper; using U = std::underlying_type_t; U mBits{0}; @@ -370,13 +383,19 @@ class EnumFlags constexpr EnumFlags(const EnumFlags&) = default; // Move constructor. constexpr EnumFlags(EnumFlags&&) = default; - // Constructor to initialize with the underlyiny type. + // 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; @@ -398,14 +417,14 @@ class EnumFlags // 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 = 2) + void set(const std::string& s, int base = DefaultBase) { - // on throw restore previous state and rethrow - const U prev = mBits; - reset(); 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) { @@ -414,7 +433,7 @@ class EnumFlags } } // Returns the raw bitset value. - constexpr auto value() const noexcept + [[nodiscard]] constexpr auto value() const noexcept { return mBits; } @@ -426,32 +445,42 @@ class EnumFlags } // Resets a specific flag. - template - requires std::is_same_v + template T> constexpr void reset(T t) { mBits &= ~to_bit(t); } // Tests if a specific flag is set. - template - requires std::is_same_v + 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 - requires std::is_same_v + 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 - requires std::is_same_v + template T> constexpr void toggle(T t) noexcept { mBits ^= to_bit(t); @@ -463,6 +492,12 @@ class EnumFlags 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 { @@ -504,27 +539,26 @@ class EnumFlags } // Checks if any flag is set (Boolean context). - constexpr explicit operator bool() const noexcept + [[nodiscard]] constexpr explicit operator bool() const noexcept { return any(); } // Check if given flag is set. - template - requires std::is_same_v - constexpr bool operator[](const T t) noexcept + template T> + [[nodiscard]] constexpr bool operator[](const T t) const noexcept { return test(t); } // Checks if two flag sets are equal. - constexpr bool operator==(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr bool operator==(const EnumFlags& o) const noexcept { return mBits == o.mBits; } // Checks if two flag sets are not equal. - constexpr bool operator!=(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr bool operator!=(const EnumFlags& o) const noexcept { return mBits != o.mBits; } @@ -536,8 +570,7 @@ class EnumFlags constexpr EnumFlags& operator=(EnumFlags&& o) = default; // Performs a bitwise OR with a flag. - template - requires std::is_same_v + template T> constexpr EnumFlags& operator|=(T t) noexcept { mBits |= to_bit(t); @@ -545,8 +578,7 @@ class EnumFlags } // Performs a bitwise AND with a flag. - template - requires std::is_same_v + template T> constexpr EnumFlags& operator&=(T t) noexcept { mBits &= to_bit(t); @@ -554,8 +586,7 @@ class EnumFlags } // Returns a flag set with a bitwise AND. - template - requires std::is_same_v + template T> constexpr EnumFlags operator&(T t) const noexcept { return EnumFlags(mBits & to_bit(t)); @@ -583,7 +614,13 @@ class EnumFlags // Performs a bitwise XOR with another flag set. constexpr EnumFlags operator^(const EnumFlags& o) const noexcept { - return Flags(mBits ^ o.mBits); + 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. @@ -595,14 +632,14 @@ class EnumFlags // Checks if all specified flags are set. template - constexpr bool all_of(Ts... flags) const noexcept + [[nodiscard]] constexpr bool all_of(Ts... flags) const noexcept { - return ((test(flags) && ...)); + return test(flags...); } // Checks if none of the specified flags are set. template - constexpr bool none_of(Ts... flags) const noexcept + [[nodiscard]] constexpr bool none_of(Ts... flags) const noexcept { return (!(test(flags) || ...)); } @@ -616,7 +653,7 @@ class EnumFlags // Deserializes a string into the flag set. void deserialize(const std::string& data) { - uint64_t v = std::stoul(data); + typename H::UMax v = std::stoul(data); if (v > H::MaxRep) { throw std::out_of_range("Values exceeds enum range."); } @@ -626,58 +663,114 @@ class EnumFlags // Counts the number of set bits (active flags). [[nodiscard]] constexpr size_t count() const noexcept { - size_t c{0}; - for (size_t i{H::Min_u_v}; i < H::Max_u_v; ++i) { - if ((mBits & (U(1) << i)) != U(0)) { - ++c; - } - } - return c; + return std::popcount(mBits); } // Returns the union of two flag sets. - constexpr EnumFlags union_with(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr EnumFlags union_with(const EnumFlags& o) const noexcept { return EnumFlags(mBits | o.mBits); } // Returns the intersection of two flag sets. - constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept + [[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. - constexpr bool contains(const EnumFlags& other) const noexcept + [[nodiscard]] constexpr bool contains(const EnumFlags& other) const noexcept { return (mBits & other.mBits) == other.mBits; } private: - // Set implemnetation, bits was zeroed before. + // 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) { // check of only 0 and 1 in string - if (!std::all_of(s.begin(), s.end(), [](char c) { return c == '0' || c == '1'; })) { + 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."); } } - uint64_t v = std::stoul(s, nullptr, base); + typename H::UMax v = std::stoul(std::string(s), nullptr, base); if (v > H::MaxRep) { - throw std::out_of_range("Values exceeds enum range."); + throw std::out_of_range("Value exceeds enum range."); } mBits = static_cast(v); - } else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ','; })) { + } + // 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 { - char token = (s.find(',') != std::string::npos) ? ',' : '|'; - for (const auto& tok : Str::tokenize(s, token)) { + // 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 { diff --git a/Common/Utils/include/CommonUtils/StringUtils.h b/Common/Utils/include/CommonUtils/StringUtils.h index c68e441d5b1c4..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 { diff --git a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h b/Common/Utils/src/CommonUtilsTestLinkDef.h similarity index 67% rename from Framework/TestWorkflows/src/o2_sim_its_ALP3.h rename to Common/Utils/src/CommonUtilsTestLinkDef.h index f9c465fcf5717..9ee67f62fd7d0 100644 --- a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h +++ b/Common/Utils/src/CommonUtilsTestLinkDef.h @@ -9,17 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef WORKFLOWS_O2_SIM_ITS_ALP3 -#define WORKFLOWS_O2_SIM_ITS_ALP3 +#ifdef __CLING__ -#include "Framework/DataProcessorSpec.h" +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; -namespace o2 -{ -namespace workflows -{ -o2::framework::DataProcessorSpec sim_its_ALP3(); -} -} // namespace o2 +#pragma link C++ class o2::conf::test::TestParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::test::TestParam> + ; -#endif // WORKFLOWS_O2_SIM_ITS_ALP3 +#endif diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index 8e242952bd61d..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) { diff --git a/Framework/Foundation/src/Traits.cxx b/Common/Utils/src/ConfigurableParamTest.cxx similarity index 86% rename from Framework/Foundation/src/Traits.cxx rename to Common/Utils/src/ConfigurableParamTest.cxx index faff430964e73..5115a8dfe889d 100644 --- a/Framework/Foundation/src/Traits.cxx +++ b/Common/Utils/src/ConfigurableParamTest.cxx @@ -8,3 +8,6 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParamTest.h" +O2ParamImpl(o2::conf::test::TestParam); diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 4c0dd30ae6211..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; @@ -77,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); @@ -104,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/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 index 5c8b71eb9040a..9101ffb97fdfe 100644 --- a/Common/Utils/test/testEnumFlags.cxx +++ b/Common/Utils/test/testEnumFlags.cxx @@ -14,6 +14,9 @@ #define BOOST_TEST_DYN_LINK #include +#include +#include + #include #include @@ -21,13 +24,25 @@ // Example enum to use with EnumFlags enum class TestEnum : uint8_t { - Bit1, + 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; @@ -59,11 +74,22 @@ BOOST_AUTO_TEST_CASE(Flags_test) 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]); @@ -141,7 +167,7 @@ BOOST_AUTO_TEST_CASE(Flags_test) BOOST_TEST(flags.test(TestEnum::Bit4)); } - { // test with different delimiter + { // test with , delimiter std::string str = "Bit4,TestEnum::Bit2 , Bit1 "; flags.set(str); BOOST_TEST(flags.test(TestEnum::Bit1)); @@ -150,6 +176,15 @@ BOOST_AUTO_TEST_CASE(Flags_test) 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); @@ -235,6 +270,14 @@ BOOST_AUTO_TEST_CASE(Flags_test) 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)); @@ -244,6 +287,14 @@ BOOST_AUTO_TEST_CASE(Flags_test) 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}; @@ -257,4 +308,346 @@ BOOST_AUTO_TEST_CASE(Flags_test) 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/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h index e9464089d71fc..ff1462084d53d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h @@ -214,7 +214,8 @@ struct CtpCfg { uint32_t orbitShift = 0; uint32_t irInputs_1_24 = 0; uint32_t irInputs_25_48 = 0; - ClassDefNV(CtpCfg, 1) + std::vector listOfUsedInputs(); + ClassDefNV(CtpCfg, 2) }; } // namespace ctp } // namespace o2 diff --git a/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx index d899fcafec47d..5f31fe5741240 100644 --- a/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx +++ b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx @@ -254,7 +254,7 @@ void CTPRateFetcher::setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, u return; } mLHCIFdata = *ptrLHCIFdata; - std::map metadata; + std::map metadata; metadata["runNumber"] = std::to_string(mRunNumber); auto ptrConfig = ccdb->getSpecific("CTP/Config/Config", timeStamp, metadata); if (ptrConfig == nullptr) { diff --git a/DataFormats/Detectors/CTP/src/Configuration.cxx b/DataFormats/Detectors/CTP/src/Configuration.cxx index 61e51bcb20d91..98458ef06d1d3 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -1227,9 +1227,24 @@ int CtpCfg::readAndSave(std::string& path) } 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/Common/include/DetectorsCommonDataFormats/AlignParam.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h index a93a37c1e36ab..5a0d2d64b0ff5 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h @@ -55,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; 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 f20cf3dac4971..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; @@ -261,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 @@ -308,13 +308,24 @@ bool AlignParam::applyToGeometry() const TGeoHMatrix* align = new TGeoHMatrix(createMatrix()); if (mIsGlobal) { align->Multiply(node->GetMatrix()); - TGeoHMatrix* g = node->GetMatrix(node->GetLevel() - 1); align->MultiplyLeft(node->GetMatrix(node->GetLevel() - 1)->Inverse()); + } else { + align->MultiplyLeft(node->GetOriginalMatrix()); } - LOG(debug) << "Aligning volume " << symname; 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()); } //_____________________________________________________________________________ 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/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index e5331b7b739b2..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -47,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/EventsPerBc.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h new file mode 100644 index 0000000000000..9fcd1318914bd --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h @@ -0,0 +1,25 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef _FT0_EVENTS_PER_BC_CALIB_OBJECT +#define _FT0_EVENTS_PER_BC_CALIB_OBJECT + +#include "CommonConstants/LHCConstants.h" +#include + +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/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 0d3491224180c..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -56,4 +56,6 @@ #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/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/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/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/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 f36150e18fbbc..37d3ca23ddb35 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -49,7 +49,8 @@ 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; @@ -57,6 +58,9 @@ class Cluster : public o2::BaseCluster ~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); } @@ -158,9 +162,10 @@ class Cluster : public o2::BaseCluster 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, 5); + 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/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/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 1d7b10dc965f7..024d6189593e9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -115,6 +115,9 @@ class CalibdEdxCorrection /// 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/Defs.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h index 9b8853a10535d..fa04586479a22 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h @@ -97,23 +97,6 @@ enum class StatisticsType { MeanStdDev ///< Use mean and standard deviation }; -enum class PadFlags : unsigned short { - flagGoodPad = 1 << 0, ///< flag for a good pad binary 0001 - flagDeadPad = 1 << 1, ///< flag for a dead pad binary 0010 - flagUnknownPad = 1 << 2, ///< flag for unknown status binary 0100 - flagSaturatedPad = 1 << 3, ///< flag for saturated status binary 0100 - flagHighPad = 1 << 4, ///< flag for pad with extremly high IDC value - flagLowPad = 1 << 5, ///< flag for pad with extremly low IDC value - flagSkip = 1 << 6, ///< flag for defining a pad which is just ignored during the calculation of I1 and IDCDelta - flagFEC = 1 << 7, ///< flag for a whole masked FEC - flagNeighbour = 1 << 8, ///< flag if n neighbouring pads are outlier - flagAllNoneGood = flagDeadPad | flagUnknownPad | flagSaturatedPad | flagHighPad | flagLowPad | flagSkip | flagFEC | flagNeighbour, -}; - -inline PadFlags operator&(PadFlags a, PadFlags b) { return static_cast(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/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/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/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/src/CalibdEdxCorrection.cxx b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx index 0991c8693d8e8..152feacb41937 100644 --- a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx +++ b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx @@ -168,3 +168,16 @@ float CalibdEdxCorrection::getMeanEntries(const GEMstack stack, ChargeType charg 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 14c3887f8e8ae..b56d07acd7c73 100644 --- a/DataFormats/Detectors/TPC/src/DCS.cxx +++ b/DataFormats/Detectors/TPC/src/DCS.cxx @@ -329,12 +329,27 @@ void fillBuffer(std::pair, std::vector>& buffe } } - std::pair, std::vector> buffTmp{ - std::vector(buffer.first.begin() + idxStartBuffer, buffer.first.end()), - std::vector(buffer.second.begin() + idxStartBuffer, buffer.second.end())}; - - buffTmp.first.insert(buffTmp.first.end(), values.first.begin(), values.first.end()); - buffTmp.second.insert(buffTmp.second.end(), values.second.begin(), values.second.end()); + 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); } 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 353f635306e68..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 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/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/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h similarity index 72% rename from Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx rename to DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h index caa642794b561..1014b3d8c704e 100644 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h @@ -9,16 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// @file FT0DataReaderDPLSpec.cxx +#ifdef __CLING__ -#include "FT0Workflow/FT0DataReaderDPLSpec.h" +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; -using namespace o2::framework; +#pragma link C++ class o2::fd3::Hit + ; +#pragma link C++ class vector < o2::fd3::Hit> + ; -namespace o2 -{ -namespace ft0 -{ - -} // namespace ft0 -} // namespace o2 +#endif diff --git a/Detectors/Vertexing/src/FwdDCAFitterN.cxx b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx similarity index 54% rename from Detectors/Vertexing/src/FwdDCAFitterN.cxx rename to DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx index f7176aa5039fd..403a3402bd30c 100644 --- a/Detectors/Vertexing/src/FwdDCAFitterN.cxx +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx @@ -9,25 +9,27 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file DCAFitterN.cxx -/// \brief Defintions for N-prongs secondary vertex fit -/// \author ruben.shahoyan@cern.ch, adapted from central barrel to fwd rapidities by Rita Sadek, rita.sadek@cern.ch +/// \file Hit.cxx +/// \brief Implementation of the Hit class -#include "DetectorsVertexing/FwdDCAFitterN.h" +#include "DataFormatsFD3/Hit.h" +#include + +ClassImp(o2::fd3::Hit); namespace o2 { -namespace vertexing +namespace fd3 { -void __test_instance__() +void Hit::Print(const Option_t* opt) const { - FwdDCAFitter2 ft2; - FwdDCAFitter3 ft3; - o2::track::TrackParCovFwd tr; - ft2.process(tr, tr); - ft3.process(tr, tr, tr); + 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 vertexing +} // 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/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 2dbfbd67d8d6c..b44f41c5d3cb3 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -588,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 aa93414cfb99f..4f7e49acb4d98 100644 --- a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h +++ b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h @@ -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 40987c483e1b8..9770df9fa54ef 100644 --- a/DataFormats/Headers/include/Headers/Stack.h +++ b/DataFormats/Headers/include/Headers/Stack.h @@ -36,9 +36,10 @@ 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: @@ -99,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)...); @@ -142,7 +143,7 @@ struct Stack { private: 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 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/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/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h b/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h index e509be97a14fa..d0347114b5b4c 100644 --- a/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h +++ b/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h @@ -23,6 +23,7 @@ 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.). @@ -32,17 +33,36 @@ 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 + 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; - // fills and returns AggregatedRunInfo for a given run number. - static AggregatedRunInfo buildAggregatedRunInfo(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber); - static AggregatedRunInfo buildAggregatedRunInfo(int runnumber, long sorMS, long eorMS, long orbitResetMUS, const o2::parameters::GRPECSObject* grpecs, const std::vector* ctfFirstRunOrbitVec); + 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 diff --git a/DataFormats/Parameters/src/AggregatedRunInfo.cxx b/DataFormats/Parameters/src/AggregatedRunInfo.cxx index 22ce362b5d85a..40402a33af68b 100644 --- a/DataFormats/Parameters/src/AggregatedRunInfo.cxx +++ b/DataFormats/Parameters/src/AggregatedRunInfo.cxx @@ -15,13 +15,14 @@ #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(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber) +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 @@ -42,14 +43,15 @@ o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo(o2:: 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); + 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) +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 @@ -81,5 +83,69 @@ o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo(int orbitSOR = (orbitSOR / nOrbitsPerTF + 1) * nOrbitsPerTF; } } - return AggregatedRunInfo{runnumber, sorMS, eorMS, nOrbitsPerTF, orbitResetMUS, orbitSOR, orbitEOR, grpecs}; + 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/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/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/Common/DCAFitter/include/DCAFitter/HelixHelper.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h similarity index 96% rename from Common/DCAFitter/include/DCAFitter/HelixHelper.h rename to DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h index bd710e459ac54..d197cba256c0e 100644 --- a/Common/DCAFitter/include/DCAFitter/HelixHelper.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h @@ -63,9 +63,10 @@ struct CrossInfo { { 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 (o2::gpu::GPUCommonMath::Sqrt(dist) < 1e-12) { + if (dist < 1e-12) { return nDCA; // circles are concentric? } if (dist > rsum) { // circles don't touch, chose a point in between @@ -75,9 +76,13 @@ struct CrossInfo { 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 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 @@ -89,7 +94,7 @@ struct CrossInfo { 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::Sqrt(xDist) < o2::gpu::GPUCommonMath::Sqrt(yDist)) { + } 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; @@ -167,7 +172,7 @@ struct CrossInfo { /// 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(); diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h index 1816e8604c0be..7bcfd7af0911a 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h @@ -86,6 +86,14 @@ class MatchInfoTOF hasT0_1BCbefore = 0x1 << 8, hasT0_2BCbefore = 0x1 << 9 }; + void setFT0Best(double val, float res = 200.) + { + mFT0Best = val; + mFT0BestRes = res; + } + double getFT0Best() const { return mFT0Best; } + float getFT0BestRes() const { return mFT0BestRes; } + private: int mIdLocal; // track id in sector of the pair track-TOFcluster float mChi2; // chi2 of the pair track-TOFcluster @@ -106,7 +114,10 @@ class MatchInfoTOF float mTgeant = 0.0; ///< geant time in MC double mT0true = 0.0; ///< t0true - ClassDefNV(MatchInfoTOF, 8); + 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, 9); }; } // namespace dataformats } // namespace o2 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/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h index f240e34861eeb..1d6c4d9f0e4ea 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -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_ @@ -226,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(); @@ -248,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); @@ -275,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]; } @@ -293,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]; @@ -428,6 +436,7 @@ template GPUdi() void TrackParametrization::setAlpha(value_t v) { mAlpha = v; + math_utils::detail::bringToPMPi(mAlpha); } //____________________________________________________________ @@ -752,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 cd9d1517a81b1..0fc01e6db61a2 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h @@ -89,9 +89,14 @@ 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 getPredictedChi2Quiet(const dim2_t& p, const dim3_t& cov) const; @@ -118,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(); diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h index fc89f162a0727..cb1c9d5d87c7f 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h @@ -18,10 +18,13 @@ #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 std::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]; } @@ -66,6 +78,7 @@ class VertexBase GPUd() float getSigmaZ() const { return gpu::CAMath::Sqrt(getSigmaZ2()); } 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; } @@ -102,6 +115,7 @@ class VertexBase setSigmaYZ(syz); } 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); } @@ -130,10 +144,13 @@ class Vertex : public VertexBase GPUhdDefault() Vertex() = default; GPUhdDefault() ~Vertex() = default; - GPUhd() Vertex(const math_utils::Point3D& pos, const std::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; } @@ -162,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/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/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index 1bdf9b55a60a0..7fe677a6e1c7a 100644 --- a/DataFormats/Reconstruction/src/TrackParametrization.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx @@ -188,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) @@ -378,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); @@ -399,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; @@ -575,7 +615,7 @@ template std::string TrackParametrization::asString() const { // print parameters as string - return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e} |Q|:{:d} {:s}\n", + return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e} |Q|:{:d} {:s}", getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName()); } @@ -651,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) { @@ -670,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); @@ -691,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; @@ -717,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) { @@ -739,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 } @@ -753,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 } @@ -763,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 } @@ -772,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) { @@ -794,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 diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx index aee24238f1247..2f8f15f783c60 100644 --- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx @@ -43,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) @@ -52,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)) { @@ -66,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) { @@ -106,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; @@ -158,6 +255,7 @@ GPUd() bool TrackParametrizationWithError::testRotate(value_t) const // no ops return true; } + //______________________________________________________________ template GPUd() bool TrackParametrizationWithError::rotate(value_t alpha) @@ -213,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) @@ -227,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); @@ -245,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; @@ -468,8 +669,8 @@ 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()); @@ -485,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; @@ -596,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() @@ -1113,6 +1507,143 @@ 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(std::array& cv) const 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/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/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/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 b718b2d5eb804..0dc3806e52cf2 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -141,6 +141,12 @@ class DigitizationContext // 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; diff --git a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h index d2ccec147cc4f..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 { @@ -130,6 +131,30 @@ class FixedSkipBC_InteractionSampler : public InteractionSampler 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/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h index ec8e6db889167..23dc30119aa7a 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h +++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h @@ -470,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)) { 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 e5b4d6e706732..79e36aa9fa48b 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -452,18 +452,18 @@ std::vector> getTimeFrameBoundaries(std::vector(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_early_integral); + 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); @@ -577,7 +577,7 @@ void DigitizationContext::applyMaxCollisionFilter(std::vector(tf_indices) = indices_old_to_new[lastindex]; // end; } else { - std::get<1>(tf_indices) = newrecords.size(); // end; + 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 @@ -591,11 +591,6 @@ void DigitizationContext::applyMaxCollisionFilter(std::vector> DigitizationContext::calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly) const { auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF, orbitsEarly); - LOG(info) << "Fixed " << timeframeindices.size() << " timeframes "; - for (auto p : timeframeindices) { - LOG(info) << std::get<0>(p) << " " << std::get<1>(p) << " " << std::get<2>(p); - } - return timeframeindices; } @@ -635,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 @@ -693,11 +699,12 @@ DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, 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); + auto endindex = std::get<1>(tf_ranges) + 1; auto earlyindex = std::get<2>(tf_ranges); if (earlyindex >= 0) { @@ -705,7 +712,7 @@ DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, } 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) { + if (mInteractionVertices.size() >= endindex) { std::copy(mInteractionVertices.begin() + startindex, mInteractionVertices.begin() + endindex, std::back_inserter(r.mInteractionVertices)); } diff --git a/DataFormats/simulation/src/InteractionSampler.cxx b/DataFormats/simulation/src/InteractionSampler.cxx index 5e14e22e5f8db..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(); @@ -162,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 15abe9d50390f..8f74bd757e791 100644 --- a/DataFormats/simulation/src/SimulationDataLinkDef.h +++ b/DataFormats/simulation/src/SimulationDataLinkDef.h @@ -25,6 +25,7 @@ #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 7ab36d260e480..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 @@ -75,6 +76,7 @@ o2_add_executable( O2::DataFormatsFT0 O2::Steer O2::ZDCBase + O2::FrameworkAnalysisSupport nlohmann_json::nlohmann_json ) 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/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 1e3a4cf970d3b..2c58db42ed856 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -237,6 +237,8 @@ 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.}; @@ -246,7 +248,7 @@ class AODProducerWorkflowDPL : public Task 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; @@ -256,6 +258,7 @@ class AODProducerWorkflowDPL : public Task 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; diff --git a/Detectors/AOD/src/AODMcProducerHelpers.cxx b/Detectors/AOD/src/AODMcProducerHelpers.cxx index 1a01f103dcfdb..a7093e0048c25 100644 --- a/Detectors/AOD/src/AODMcProducerHelpers.cxx +++ b/Detectors/AOD/src/AODMcProducerHelpers.cxx @@ -305,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; @@ -354,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 1cc89d54a030d..852419a9895eb 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -363,10 +363,10 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs { tracksQACursor( trackQAInfoHolder.trackID, - truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + mTrackQCRetainOnlydEdx ? 0.0f : truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), truncateFloatFraction(trackQAInfoHolder.tpcdEdxNorm, mTrackSignal), - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaR, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaZ, trackQAInfoHolder.tpcClusterByteMask, trackQAInfoHolder.tpcdEdxMax0R, trackQAInfoHolder.tpcdEdxMax1R, @@ -376,18 +376,18 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs trackQAInfoHolder.tpcdEdxTot1R, trackQAInfoHolder.tpcdEdxTot2R, trackQAInfoHolder.tpcdEdxTot3R, - trackQAInfoHolder.dRefContY, - trackQAInfoHolder.dRefContZ, - trackQAInfoHolder.dRefContSnp, - trackQAInfoHolder.dRefContTgl, - trackQAInfoHolder.dRefContQ2Pt, - trackQAInfoHolder.dRefGloY, - trackQAInfoHolder.dRefGloZ, - trackQAInfoHolder.dRefGloSnp, - trackQAInfoHolder.dRefGloTgl, - trackQAInfoHolder.dRefGloQ2Pt, - trackQAInfoHolder.dTofdX, - trackQAInfoHolder.dTofdZ); + 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 @@ -499,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) { @@ -540,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); @@ -947,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, @@ -982,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 @@ -997,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()) { @@ -1012,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); } } } @@ -1026,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]) { @@ -1035,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; @@ -1059,7 +1063,8 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& source == 0, // background mMcParticleW, mMcParticleMom, - mMcParticlePos); + mMcParticlePos, + mUseSigFiltMC); mcReader.releaseTracksForSourceAndEvent(source, event); } @@ -1126,7 +1131,7 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr if (!needToStore(mGIDToTableID)) { continue; } - if (mcTruth.isValid()) { // if not set, -1 will be stored + 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); @@ -1139,6 +1144,21 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr } } } + 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); + } + } + } else if (mcTruth.isNoise()) { labelHolder.labelMask |= (0x1 << 14); } @@ -1674,11 +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"); - mUser = ic.options().get("created-by"); - 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"); @@ -1688,6 +1708,7 @@ 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); @@ -1698,6 +1719,8 @@ void AODProducerWorkflowDPL::init(InitContext& ic) 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"); @@ -1727,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!"; @@ -2041,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++) { @@ -2600,6 +2647,12 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac 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 = dEdx.dEdxTotTPC; @@ -2717,7 +2770,7 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int 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; + 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) && @@ -3299,9 +3352,12 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo 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"}}, @@ -3309,7 +3365,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo 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/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 7681380692033..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; 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/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 96ee2e4fcf418..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 diff --git a/Detectors/Align/src/AlignableDetectorTPC.cxx b/Detectors/Align/src/AlignableDetectorTPC.cxx index b3d2102559974..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" 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/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 0ba2905ab02ec..83a9193274e4f 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/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(DetectorsBase SOURCES src/Detector.cxx @@ -28,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 @@ -45,6 +48,7 @@ o2_add_library(DetectorsBase O2::GPUDataTypes MC::VMC TBB::tbb + ROOT::Gdml ) o2_target_root_dictionary(DetectorsBase @@ -61,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) @@ -87,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 @@ -96,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..593bf37df5879 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 @@ -325,13 +326,12 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) } } } - 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 {}", mExtHeader.asString(), mDictOpt); } else { LOGP(info, "Internal per-TF CTF Dict will be created"); } diff --git a/Detectors/Base/include/DetectorsBase/GeometryManagerParam.h b/Detectors/Base/include/DetectorsBase/GeometryManagerParam.h index c41d41e25e233..b82d526344646 100644 --- a/Detectors/Base/include/DetectorsBase/GeometryManagerParam.h +++ b/Detectors/Base/include/DetectorsBase/GeometryManagerParam.h @@ -23,6 +23,8 @@ struct GeometryManagerParam : public o2::conf::ConfigurableParamHelpermLayers[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 @@ -98,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 dbdef47e4edc0..75b9446aebade 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -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, @@ -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/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h similarity index 51% rename from Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h rename to Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h index 3bbab66d16497..5ec85f1c14702 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h +++ b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h @@ -9,20 +9,30 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_FDD_RAWWORKFLOW_H -#define O2_FDD_RAWWORKFLOW_H +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo -/// @file RawWorkflow.h +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ -#include "Framework/WorkflowSpec.h" +class TGeoShape; +class TGeoTessellated; namespace o2 { -namespace fdd +namespace base { -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut); -} // namespace fdd + +/// 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/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index bd76e9bfbe2e4..8255c143ebb4a 100644 --- a/Detectors/Base/src/DetectorsBaseLinkDef.h +++ b/Detectors/Base/src/DetectorsBaseLinkDef.h @@ -42,4 +42,6 @@ #pragma link C++ class o2::data::Stack + ; +#pragma link C++ class o2::base::O2Tessellated - ; + #endif diff --git a/Detectors/Base/src/GeometryManager.cxx b/Detectors/Base/src/GeometryManager.cxx index c5e7e8e47e731..a067767752a69 100644 --- a/Detectors/Base/src/GeometryManager.cxx +++ b/Detectors/Base/src/GeometryManager.cxx @@ -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 1c44cea65c69c..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; @@ -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, @@ -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 @@ -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, @@ -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; @@ -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::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; @@ -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,12 +1092,26 @@ 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; 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 GPUCA_GPUCODE template class PropagatorImpl; 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..a5f9d0eac90e8 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(), 1.f, o2::detectors::DetID::CPV, 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..6f9445d9ddd16 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, "ccdb", {"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/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/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index ab03649c0646b..081e6cf4d968a 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -32,6 +32,7 @@ struct CTFReaderInp { std::string metricChannel{}; std::string fileIRFrames{}; std::string fileRunTimeSpans{}; + std::string dictOpt{}; std::vector ctfIDs{}; bool reverseCTFIDs{false}; bool skipSkimmedOutTF = false; diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index e502b88611a3c..4100ebb37c61d 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -112,7 +112,7 @@ class CTFReaderSpec : public o2::framework::Task int mCTFCounterAcc = 0; int mNFailedFiles = 0; int mFilesRead = 0; - int mTFLength = 128; + int mTFLength = 32; int mNWaits = 0; int mRunNumberPrev = -1; long mTotalWaitTime = 0; @@ -234,7 +234,7 @@ void CTFReaderSpec::loadRunTimeSpans(const std::string& flname) { std::ifstream inputFile(flname); if (!inputFile) { - LOGP(fatal, "Failed to open selected run/timespans file {}", mInput.fileRunTimeSpans); + LOGP(fatal, "Failed to open selected run/timespans file {}", flname); } std::string line; size_t cntl = 0, cntr = 0; @@ -286,7 +286,7 @@ void CTFReaderSpec::loadRunTimeSpans(const std::string& flname) logError(); } } - LOGP(info, "Read {} time-spans for {} runs from {}", cntr, mRunTimeRanges.size(), mInput.fileRunTimeSpans); + LOGP(info, "Read {} time-spans for {} runs from {}", cntr, mRunTimeRanges.size(), flname); inputFile.close(); } @@ -645,7 +645,6 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) 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"}}); diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index cddf694251a01..fc50c971c5d20 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -52,6 +52,7 @@ 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{"loop", VariantType::Int, 0, {"loop N times (infinite for N<0)"}}); @@ -132,6 +133,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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")); @@ -181,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/GetAndSave.C b/Detectors/CTP/macro/GetAndSave.C index 345bb1caf4a96..ff70a3055c957 100644 --- a/Detectors/CTP/macro/GetAndSave.C +++ b/Detectors/CTP/macro/GetAndSave.C @@ -44,7 +44,7 @@ void GetAndSave(std::string ccdbHost = "http://ccdb-test.cern.ch:8080") 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/PlotPbLumi.C b/Detectors/CTP/macro/PlotPbLumi.C index 04666d5bd1cf6..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 @@ -30,12 +30,21 @@ #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 @@ -43,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; @@ -65,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; @@ -85,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(); @@ -104,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; @@ -120,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; @@ -133,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"); @@ -168,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/TestFetcher.C b/Detectors/CTP/macro/TestFetcher.C index 2d73b83cd174e..b2b6912f49911 100644 --- a/Detectors/CTP/macro/TestFetcher.C +++ b/Detectors/CTP/macro/TestFetcher.C @@ -27,7 +27,7 @@ void TestFetcher(int runNumber = 557251) fetcher.setupRun(runNumber, &ccdb, ts, 0); ccdb.setURL("http://ali-qcdb-gpn.cern.ch:8083/"); std::string QCDBPathCTPScalers = "qc/CTP/Scalers"; - map metadata; // can be empty + std::map metadata; // can be empty std::string run = std::to_string(runNumber); metadata["runNumber"] = run; CTPRunScalers* ctpscalers = ccdb.getSpecific(QCDBPathCTPScalers, ts, metadata); diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 9189df5d12685..b17db0e77be28 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -34,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(), 1.f, o2::detectors::DetID::CTP, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/CTP/simulation/src/Digitizer.cxx b/Detectors/CTP/simulation/src/Digitizer.cxx index 55893cb0269da..b1d4ef40b7b0e 100644 --- a/Detectors/CTP/simulation/src/Digitizer.cxx +++ b/Detectors/CTP/simulation/src/Digitizer.cxx @@ -194,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 eee7abb08d16c..dda45c9f11a34 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ 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; @@ -42,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/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index 8c2f5d05aa031..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(); @@ -90,7 +89,7 @@ void EntropyDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& } } -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}, @@ -99,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")); + + 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/entropy-encoder-workflow.cxx b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx index 1fcaa89be9888..9057d16df4384 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, "ccdb", {"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/workflowScalers/CMakeLists.txt b/Detectors/CTP/workflowScalers/CMakeLists.txt index a31774ac66d69..f02a7f33e2abd 100644 --- a/Detectors/CTP/workflowScalers/CMakeLists.txt +++ b/Detectors/CTP/workflowScalers/CMakeLists.txt @@ -34,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/ctpCCDBManager.h b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h index 4237ad4501fcc..df2aa79d18697 100644 --- a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h +++ b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h @@ -31,8 +31,9 @@ class ctpCCDBManager int saveOrbitReset(long timeStamp); int saveCtpCfg(uint32_t runNumber, long timeStamp); 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); + 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; }; diff --git a/Detectors/CTP/workflowScalers/src/RunManager.cxx b/Detectors/CTP/workflowScalers/src/RunManager.cxx index 054505aea7ba6..778677bec2ec9 100644 --- a/Detectors/CTP/workflowScalers/src/RunManager.cxx +++ b/Detectors/CTP/workflowScalers/src/RunManager.cxx @@ -216,14 +216,14 @@ int CTPRunManager::addScalers(uint32_t irun, std::time_t time, bool start) } scalrec.intRecord.bc = 0; mActiveRuns[irun]->scalers.addScalerRacordRaw(scalrec); - LOG(info) << "Adding scalers for orbit:" << scalrec.intRecord.orbit; + 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(info) << "Processing message with topic:" << topic; + LOG(debug) << "Processing message with topic:" << topic; std::string firstcounters; if (topic.find("clear") != std::string::npos) { mRunsLoaded.clear(); @@ -283,7 +283,7 @@ int CTPRunManager::processMessage(std::string& topic, const std::string& message // get config size_t irun = message.find("run"); if (irun == std::string::npos) { - LOG(warning) << "run keyword not found in SOX"; + LOG(debug) << "run keyword not found in SOX"; irun = message.size(); } LOG(info) << "SOX received, Run keyword position:" << irun; @@ -319,22 +319,22 @@ int CTPRunManager::processMessage(std::string& topic, const std::string& message } 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)); + 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 << mCounters[i - 1] << " "; } } - std::cout << std::endl; - LOG(info) << "Counter size:" << tokens.size(); + // 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(info) << "Run continue:" << mCounters[i]; + LOG(debug) << "Run continue:" << mCounters[i]; addScalers(i, tt); // LOG(info) << " QC period:" << mActiveRunNumbers[i] << " " << mActiveRuns[i]->qcwpcount << " " << mQCWritePeriod; if (mActiveRuns[i]->qcwpcount > mQCWritePeriod) { @@ -367,7 +367,7 @@ int CTPRunManager::processMessage(std::string& topic, const std::string& message } } mEOX = 0; - printActiveRuns(); + // printActiveRuns(); return 0; } void CTPRunManager::printActiveRuns() const 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 7dedcacbf6047..aa953e89264ef 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx @@ -124,7 +124,12 @@ int main(int argc, char** argv) ret = api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); } else { std::cout << "Storing:" << ccdbPath << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; + std::string filename = "orbitReset.root"; + auto classname = "std::vector"; + metadata["adjustableEOV"] = "true"; ret = api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + o2::ccdb::CcdbObjectInfo oi(ccdbPath, classname, filename, metadata, tmin, tmax); + adjustOverriddenEOV(api, oi); } } // diff --git a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx index f8f8ad3c95fbb..391d1b5ccf58b 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx @@ -56,6 +56,7 @@ InjectorFunction dcs2dpl(std::string& ccdbhost, std::string& bkhost, std::string 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 @@ -66,7 +67,15 @@ InjectorFunction dcs2dpl(std::string& ccdbhost, std::string& bkhost, std::string 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; }; diff --git a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx index 58850d88eb2c6..74d4a905c93e2 100644 --- a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx +++ b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx @@ -28,7 +28,7 @@ int ctpCCDBManager::saveRunScalersToCCDB(CTPRunScalers& scalers, long timeStart, { // data base if (mCCDBHost == "none") { - LOG(info) << "Scalers not written to CCDB none"; + LOG(debug) << "Scalers not written to CCDB none"; return 0; } // CTPActiveRun* run = mActiveRuns[i]; @@ -40,7 +40,7 @@ int ctpCCDBManager::saveRunScalersToCCDB(CTPRunScalers& scalers, long timeStart, long tmin = timeStart - time10min; long tmax = timeStop + time3days; o2::ccdb::CcdbApi api; - map metadata; // can be empty + 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 @@ -56,7 +56,7 @@ int ctpCCDBManager::saveRunScalersToQCDB(CTPRunScalers& scalers, long timeStart, { // data base if (mQCDBHost == "none") { - LOG(info) << "Scalers not written to QCDB none"; + LOG(debug) << "Scalers not written to QCDB none"; return 0; } // CTPActiveRun* run = mActiveRuns[i];q @@ -68,7 +68,7 @@ int ctpCCDBManager::saveRunScalersToQCDB(CTPRunScalers& scalers, long timeStart, long tmin = timeStart - time10min; long tmax = timeStop + time3days; o2::ccdb::CcdbApi api; - map metadata; // can be empty + 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 @@ -95,7 +95,7 @@ int ctpCCDBManager::saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart) long tmin = timeStart - time10min; long tmax = timeStart + time3days; o2::ccdb::CcdbApi api; - map metadata; // can be empty + 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 @@ -125,7 +125,7 @@ int ctpCCDBManager::saveSoxOrbit(uint32_t runNumber, uint32_t soxOrbit, long tim long tmin = timestamp / 1000; long tmax = tmin + 381928219; o2::ccdb::CcdbApi api; - map metadata; // can be empty + 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 @@ -155,11 +155,16 @@ int ctpCCDBManager::saveOrbitReset(long timeStamp) long tmin = timeStamp / 1000; long tmax = tmin + 381928219; o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty 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, mCCDBPathOrbitReset, metadata, tmin, tmax); + // 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 { @@ -184,7 +189,7 @@ int ctpCCDBManager::saveCtpCfg(uint32_t runNumber, long timeStart) long tmin = timeStart - time10min; long tmax = timeStart + time3days; o2::ccdb::CcdbApi api; - map metadata; // can be empty + 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 @@ -199,10 +204,16 @@ int ctpCCDBManager::saveCtpCfg(uint32_t runNumber, long timeStart) } CTPConfiguration ctpCCDBManager::getConfigFromCCDB(long timestamp, std::string run, bool& ok) { + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(mCCDBHost); - map metadata; // can be empty + 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; @@ -228,7 +239,7 @@ CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run { auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(mCCDBHost); - map metadata; // can be empty + std::map metadata; // can be empty metadata["runNumber"] = run; auto ctpscalers = mgr.getSpecific(mCCDBPathCTPScalers, timestamp, metadata); if (ctpscalers == nullptr) { @@ -240,3 +251,24 @@ CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run } 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/include/DetectorsCalibration/IntegratedClusterCalibrator.h b/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h index 8a6996c35f2b3..9720142d391b1 100644 --- a/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h +++ b/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h @@ -331,6 +331,9 @@ struct TimeSeriesdEdx { }; struct TimeSeriesITSTPC { + float mVDrift = 0; ///< drift velocity in cm/us + float mPressure = 0; ///< pressure + float mTemperature = 0; ///< temperature TimeSeries mTSTPC; ///< TPC standalone DCAs TimeSeries mTSITSTPC; ///< ITS-TPC standalone DCAs ITSTPC_Matching mITSTPCAll; ///< ITS-TPC matching efficiency for ITS standalone + afterburner @@ -499,7 +502,7 @@ struct TimeSeriesITSTPC { nVertexContributors_Quantiles.resize(nTotalQ); } - ClassDefNV(TimeSeriesITSTPC, 5); + ClassDefNV(TimeSeriesITSTPC, 6); }; } // end namespace tpc diff --git a/Detectors/DCS/src/DataPointCreator.cxx b/Detectors/DCS/src/DataPointCreator.cxx index 06b0321ad2c94..0bfb5bcd7d387 100644 --- a/Detectors/DCS/src/DataPointCreator.cxx +++ b/Detectors/DCS/src/DataPointCreator.cxx @@ -37,10 +37,9 @@ DataPointCompositeObject createDataPointCompositeObject(const std::string& alias template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, float val, uint32_t seconds, uint16_t msec, uint16_t flags) { - float tmp[2]; - tmp[0] = val; - tmp[1] = 0; - return createDPCOM(alias, reinterpret_cast(&tmp[0]), seconds, msec, flags, DeliveryType::DPVAL_FLOAT); + uint64_t tmp = 0; + memcpy(&tmp, &val, sizeof(val)); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_FLOAT); } template <> @@ -54,36 +53,38 @@ template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, uint32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, long long val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{static_cast(val)}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, char val, uint32_t seconds, uint16_t msec, uint16_t flags) { - return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::DPVAL_CHAR); + uint64_t tmp = 0; + memcpy(&tmp, &val, 1); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_CHAR); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, bool val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_BOOL); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_BOOL); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, std::string val, uint32_t seconds, uint16_t msec, uint16_t flags) { constexpr int N{56}; - char str[N]; - strncpy(str, val.c_str(), N); - return createDPCOM(alias, reinterpret_cast(&str[0]), seconds, msec, flags, DeliveryType::DPVAL_STRING); + uint64_t tmp[N / sizeof(uint64_t)]; + strncpy((char*)tmp, val.c_str(), N); + return createDPCOM(alias, tmp, seconds, msec, flags, DeliveryType::DPVAL_STRING); } } // namespace o2::dcs diff --git a/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h b/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h index a7e81d38838a3..0c3438042ca77 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h +++ b/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h @@ -401,6 +401,10 @@ class ClusterFactory /// in cell units void evalElipsAxis(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const; + /// + /// Calculate the number of local maxima in the cluster + void evalNExMax(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const; + /// /// Time is set to the time of the digit with the maximum energy void evalTime(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const; diff --git a/Detectors/EMCAL/base/include/EMCALBase/Geometry.h b/Detectors/EMCAL/base/include/EMCALBase/Geometry.h index b4621d4b6e434..d07f42689bf7a 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/Geometry.h +++ b/Detectors/EMCAL/base/include/EMCALBase/Geometry.h @@ -429,6 +429,14 @@ class Geometry /// \return Position (0 - phi, 1 - eta) of the cell inside teh supermodule std::tuple GetCellPhiEtaIndexInSModule(int supermoduleID, int moduleID, int phiInModule, int etaInModule) const; + /// \brief Get topological row and column of cell in SM (same as for clusteriser with artifical gaps) + /// \param supermoduleID super module number + /// \param moduleID module number + /// \param phiInModule index in phi direction in module + /// \param etaInModule index in phi direction in module + /// \return tuple with (row, column) of the cell, which is global numbering scheme + std::tuple GetTopologicalRowColumn(int supermoduleID, int moduleID, int phiInModule, int etaInModule) const; + /// \brief Adapt cell indices in supermodule to online indexing /// \param supermoduleID super module number of the channel/cell /// \param iphi row/phi cell index, modified for DCal diff --git a/Detectors/EMCAL/base/src/ClusterFactory.cxx b/Detectors/EMCAL/base/src/ClusterFactory.cxx index 342f54fd94591..1752e5c0e98ee 100644 --- a/Detectors/EMCAL/base/src/ClusterFactory.cxx +++ b/Detectors/EMCAL/base/src/ClusterFactory.cxx @@ -120,6 +120,9 @@ o2::emcal::AnalysisCluster ClusterFactory::buildCluster(int clusterIn evalElipsAxis(inputsIndices, clusterAnalysis); evalDispersion(inputsIndices, clusterAnalysis); + // evaluate number of local maxima + evalNExMax(inputsIndices, clusterAnalysis); + evalCoreEnergy(inputsIndices, clusterAnalysis); evalTime(inputsIndices, clusterAnalysis); @@ -489,6 +492,64 @@ void ClusterFactory::evalCoreEnergy(gsl::span inputsIndice clusterAnalysis.setCoreEnergy(coreEnergy); } +/// +/// Calculate the number of local maxima in the cluster +//____________________________________________________________________________ +template +void ClusterFactory::evalNExMax(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const +{ + // Pre-compute cell indices and energies for all cells in cluster to avoid multiple expensive geometry lookups + const size_t n = inputsIndices.size(); + std::vector rows; + std::vector columns; + std::vector energies; + + rows.reserve(n); + columns.reserve(n); + energies.reserve(n); + + for (auto iInput : inputsIndices) { + auto [nSupMod, nModule, nIphi, nIeta] = mGeomPtr->GetCellIndex(mInputsContainer[iInput].getTower()); + + // get a nice topological indexing that is done in exactly the same way as used by the clusterizer + // this way we can handle the shared cluster cases correctly + const auto [row, column] = mGeomPtr->GetTopologicalRowColumn(nSupMod, nModule, nIphi, nIeta); + + rows.push_back(row); + columns.push_back(column); + energies.push_back(mInputsContainer[iInput].getEnergy()); + } + + // Now find local maxima using pre-computed data + int nExMax = 0; + for (size_t i = 0; i < n; i++) { + // this cell is assumed to be local maximum unless we find a higher energy cell in the neighborhood + bool isExMax = true; + + // loop over all other cells in cluster + for (size_t j = 0; j < n; j++) { + if (i == j) { + continue; + } + + // adjacent cell is any cell with adjacent phi or eta index + if (std::abs(rows[i] - rows[j]) <= 1 && + std::abs(columns[i] - columns[j]) <= 1) { + + // if there is a cell with higher energy than the current cell, it is not a local maximum + if (energies[j] > energies[i]) { + isExMax = false; + break; + } + } + } + if (isExMax) { + nExMax++; + } + } + clusterAnalysis.setNExMax(nExMax); +} + /// /// Calculates the axis of the shower ellipsoid in eta and phi /// in cell units diff --git a/Detectors/EMCAL/base/src/Geometry.cxx b/Detectors/EMCAL/base/src/Geometry.cxx index 6039c18dd34e4..3707e22f2da57 100644 --- a/Detectors/EMCAL/base/src/Geometry.cxx +++ b/Detectors/EMCAL/base/src/Geometry.cxx @@ -1103,6 +1103,30 @@ std::tuple Geometry::GetCellPhiEtaIndexInSModule(int supermoduleID, in return std::make_tuple(phiInSupermodule, etaInSupermodule); } +std::tuple Geometry::GetTopologicalRowColumn(int supermoduleID, int moduleID, int phiInModule, int etaInModule) const +{ + auto [iphi, ieta] = GetCellPhiEtaIndexInSModule(supermoduleID, moduleID, phiInModule, etaInModule); + int row = iphi; + int column = ieta; + + // Add shifts wrt. supermodule and type of calorimeter + // NOTE: + // * Rows (phi) are arranged that one space is left empty between supermodules in phi + // This is due to the physical gap that forbids clustering + // * For DCAL, there is an additional empty column between two supermodules in eta + // Again, this is to account for the gap in DCAL + + row += supermoduleID / 2 * (24 + 1); + // In DCAL, leave a gap between two SMs with same phi + if (!IsDCALSM(supermoduleID)) { // EMCAL + column += supermoduleID % 2 * 48; + } else { + column += supermoduleID % 2 * (48 + 1); + } + + return std::make_tuple(static_cast(row), static_cast(column)); +} + std::tuple Geometry::ShiftOnlineToOfflineCellIndexes(Int_t supermoduleID, Int_t iphi, Int_t ieta) const { if (supermoduleID == 13 || supermoduleID == 15 || supermoduleID == 17) { @@ -1790,7 +1814,7 @@ void Geometry::SetMisalMatrixFromCcdb(const char* path, int timestamp) const { LOG(info) << "Using CCDB to obtain EMCal alignment."; o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init("http://alice-ccdb.cern.ch"); TObjArray* matrices = api.retrieveFromTFileAny(path, metadata, timestamp); diff --git a/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h b/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h index 571b43d05ef08..ea8a0445bbe5e 100644 --- a/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h +++ b/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h @@ -62,7 +62,7 @@ class CellRecalibrator public: /// \class CellTypeException /// \brief Handling of invalid cell types in calibration - class CellTypeException : public std::exception + class CellTypeException final : public std::exception { public: /// \brief Constructor @@ -73,7 +73,7 @@ class CellRecalibrator /// \brief Get error message of the exception /// \return Error message - const char* what() const noexcept final + [[nodiscard]] char const* what() const noexcept final { return "Only possible to calibrate cells of type high gain or low gain"; } @@ -208,4 +208,4 @@ std::ostream& operator<<(std::ostream& in, const CellRecalibrator& calib); } // namespace o2 -#endif // !ALCEO2_EMCAL_CELLRECALIBRATOR_H \ No newline at end of file +#endif // !ALCEO2_EMCAL_CELLRECALIBRATOR_H diff --git a/Detectors/EMCAL/calibration/CMakeLists.txt b/Detectors/EMCAL/calibration/CMakeLists.txt index 68c8fd1eb69c7..7fec9fcef0f93 100644 --- a/Detectors/EMCAL/calibration/CMakeLists.txt +++ b/Detectors/EMCAL/calibration/CMakeLists.txt @@ -20,6 +20,7 @@ o2_add_library(EMCALCalibration src/PedestalCalibDevice.cxx src/PedestalProcessorDevice.cxx src/PedestalProcessorData.cxx + src/EMCALTempCalibExtractor.cxx PUBLIC_LINK_LIBRARIES O2::CCDB O2::EMCALBase O2::EMCALCalib O2::EMCALReconstruction @@ -46,6 +47,7 @@ o2_target_root_dictionary(EMCALCalibration include/EMCALCalibration/EMCDCSProcessor.h include/EMCALCalibration/EMCALPedestalHelper.h include/EMCALCalibration/PedestalProcessorData.h + include/EMCALCalibration/EMCALTempCalibExtractor.h LINKDEF src/EMCALCalibrationLinkDef.h) o2_add_executable(emcal-channel-calib-workflow diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h new file mode 100644 index 0000000000000..5dbaec4c933f8 --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h @@ -0,0 +1,93 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \class EMCALTempCalibExtractor +/// \brief Calculate gain correction factors based on the temperature and the cell-by-cell temperature dependent correction factors (slope and intercept) +/// \author Joshua König +/// \ingroup EMCALCalib +/// \since June 30, 2025 + +#ifndef EMCALTEMPCALIBEXTRACTOR_H_ +#define EMCALTEMPCALIBEXTRACTOR_H_ + +#include +#include +#include +#include "CCDB/BasicCCDBManager.h" +#include "EMCALCalib/ElmbData.h" +#include "EMCALCalib/TempCalibrationParams.h" +#include "EMCALBase/Geometry.h" + +namespace o2 +{ +namespace emcal +{ + +class EMCALTempCalibExtractor +{ + + public: + /// \brief Constructor + EMCALTempCalibExtractor() + { + LOG(info) << "initialized EMCALTempCalibExtractor"; + try { + // Try to access geometry initialized ountside + mGeometry = o2::emcal::Geometry::GetInstance(); + } catch (o2::emcal::GeometryNotInitializedException& e) { + mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); // fallback option + } + }; + /// \brief Destructor + ~EMCALTempCalibExtractor() = default; + + /// \brief Initialize temperature data and slope for each cell from the ccdb + /// \param path path to the slope data + /// \param timestamp timestamp for the ccdb objects or runnumber (will detect automatically if its a runnumber and convert it) + void InitializeFromCCDB(std::string path, uint64_t timestamp); + + /// \brief get average temperature in a supermodule + /// \param iSM SM number + /// \param ElmbData object where temperature sensor values are stored + /// \return average temperature in a supermodule + float getTemperatureForSM(const unsigned short iSM, o2::emcal::ElmbData* ElmbData) const; + + /// \brief get gain calibration factor depending on the temperature and the slope of the cell + /// \param cellID cell ID + /// \return gain calibration factor + float getGainCalibFactor(const unsigned short cellID) const; + + /// \brief set temperature range in which sensor ddata is assumed to be good + /// \param low lower temperature + /// \param high upper temperature + void setAcceptedEnergyRange(float low, float high); + + /// \brief set if median (true) or mean (false) should be used for averaging of the temperature in a SM + void setUseMedian(const bool tmp) { mUseMedian = tmp; } + + /// \brief get sensor IDs for a specific supermodule + /// \param iSM SM number + /// \return vector of sensor IDs + std::vector getSensorsForSM(const unsigned short iSM) const; + + private: + static constexpr unsigned short mNCells = 17664; ///< Number of EMCal cells + std::array mGainCalibFactors; ///< gain calibration factors that are calculated based on the temperature and the slopes for each cell + o2::emcal::Geometry* mGeometry; ///< pointer to the EMCal geometry + std::array mAcceptedTempRange = {15., 30.}; ///< Temperature range where sensors are believed to send good data. Temperatures outside this range will be rejected + bool mUseMedian = true; /// switch to decide if temperature within a SM should be calculated as the mean or the median of the individual sensor data +}; + +} // namespace emcal + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx b/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx new file mode 100644 index 0000000000000..02e25696f161d --- /dev/null +++ b/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx @@ -0,0 +1,127 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "EMCALCalibration/EMCALTempCalibExtractor.h" +#include "EMCALCalib/CalibDB.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include + +namespace o2 +{ +namespace emcal +{ + +void EMCALTempCalibExtractor::InitializeFromCCDB(std::string path, uint64_t timestamp) +{ + + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + uint64_t maxRunNr = 1000000; + if (timestamp < maxRunNr) { + LOG(info) << "assuming input is run " << timestamp << " will convert it to timstamp"; + auto [sor, eor] = ccdbMgr.getRunDuration(timestamp); + uint64_t sixtySec = 60000; + timestamp = eor - sixtySec; // safety margin of 1min at EOR + LOG(info) << "set timestamp to " << timestamp; + } + + o2::emcal::CalibDB calibdb("http://alice-ccdb.cern.ch"); + std::map metadata; + auto tempSensorData = calibdb.readTemperatureSensorData(timestamp, metadata); + + // also obtain cell dependent correction factors + TempCalibrationParams* params = ccdbMgr.getForTimeStamp(path, timestamp); + + std::map mapSMTemperature; + for (unsigned short i = 0; i < mNCells; ++i) { + const unsigned short iSM = mGeometry->GetSuperModuleNumber(i); + if (mapSMTemperature.count(iSM) == 0) { + mapSMTemperature[iSM] = getTemperatureForSM(iSM, tempSensorData); + } + float corrFac = params->getTempCalibParamA0(i) + params->getTempCalibParamSlope(i) * mapSMTemperature[iSM]; + mGainCalibFactors[i] = corrFac; + } +} + +float EMCALTempCalibExtractor::getTemperatureForSM(const unsigned short iSM, o2::emcal::ElmbData* ElmbData) const +{ + if (iSM < 0 || iSM > 20) { + LOG(error) << "SM " << iSM << "does not exist!"; // could be replaced with a proper exception + return 0.; + } + std::vector vecSensorID = getSensorsForSM(iSM); + + // Obtain temperature for these sensors + std::vector vecTemperature; + for (const auto& iSensor : vecSensorID) { + float temp = ElmbData->getMean(iSensor); + if (temp < mAcceptedTempRange[0] || temp > mAcceptedTempRange[1]) { + continue; + } + vecTemperature.push_back(temp); + } + + const unsigned int nEntries = vecTemperature.size(); + if (nEntries == 0) { + LOG(warning) << "No sensor data between " << mAcceptedTempRange[0] << " and " << mAcceptedTempRange[1] << "degree found... for SM " << iSM << " Setting to default 20 degree"; + return 20.; // + } + + // get median energy + float tempSM = 0.; + if (mUseMedian) { + std::sort(vecTemperature.begin(), vecTemperature.end()); + if (nEntries % 2 == 0) { + // even number of elements: average the two middle ones + tempSM = (vecTemperature[nEntries / 2 - 1] + vecTemperature[nEntries / 2]) / 2.0; + } else { + // odd number of elements: return the middle one + tempSM = vecTemperature[nEntries / 2]; + } + } else { // use Mean temperature + float sum = std::accumulate(vecTemperature.begin(), vecTemperature.end(), 0.0); + tempSM = sum / vecTemperature.size(); + } + return tempSM; +} + +float EMCALTempCalibExtractor::getGainCalibFactor(const unsigned short cellID) const +{ + if (cellID >= mNCells) { + LOG(error) << "cell ID" << cellID << " does not exist"; + return 1.; + } + return mGainCalibFactors[cellID]; +} + +std::vector EMCALTempCalibExtractor::getSensorsForSM(const unsigned short iSM) const +{ + unsigned short nSensors = 8; + if (iSM == 10 || iSM == 11 || iSM == 18 || iSM == 19) { // 1/3 SM of EMCal only have 4 sensors + nSensors = 4; + } + + std::vector vecSensorID; + for (unsigned short iELMBSensor = iSM * 8; iELMBSensor < iSM * 8 + nSensors; iELMBSensor++) { + vecSensorID.push_back(iELMBSensor); + } + return vecSensorID; +} + +void EMCALTempCalibExtractor::setAcceptedEnergyRange(float low, float high) +{ + mAcceptedTempRange[0] = low; + mAcceptedTempRange[1] = high; +} + +} // namespace emcal + +} // namespace o2 \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 1617a9f1a7d54..6584775057d9f 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -32,10 +32,10 @@ namespace o2 namespace emcal { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::EMC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::EMC, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx b/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx deleted file mode 100644 index 6fc119dc69521..0000000000000 --- a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file rawReaderFileNew.cxx -/// \author Markus Fasel , Oak Ridge National Laboratory - -#include -#include - -#include - -#include "DetectorsRaw/RawFileReader.h" -#include "DetectorsRaw/RDHUtils.h" -#include "EMCALBase/Mapper.h" -#include "EMCALBase/TriggerMappingV2.h" -#include "EMCALReconstruction/AltroDecoder.h" -#include "EMCALReconstruction/RawReaderMemory.h" -#include - -namespace bpo = boost::program_options; -// using namespace o2::emcal; - -int main(int argc, char** argv) -{ - bpo::variables_map vm; - bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + - " \n" - " Tool will decode the DDLx data for EMCAL 0\n" - "Commands / Options"); - bpo::options_description opt_hidden(""); - bpo::options_description opt_all; - bpo::positional_options_description opt_pos; - - try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbose,v", bpo::value()->default_value(0), "Select verbosity level [0 = no output]"); - add_option("version", "Print version information"); - add_option("input-file,i", bpo::value()->required(), "Specifies input file."); - add_option("debug,d", bpo::value()->default_value(0), "Select debug output level [0 = no debug output]"); - - opt_all.add(opt_general).add(opt_hidden); - bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); - - if (vm.count("help") || argc == 1) { - std::cout << opt_general << std::endl; - exit(0); - } - - if (vm.count("version")) { - // std::cout << GitInfo(); - exit(0); - } - - bpo::notify(vm); - } catch (bpo::error& e) { - std::cerr << "ERROR: " << e.what() << std::endl - << std::endl; - std::cerr << opt_general << std::endl; - exit(1); - } catch (std::exception& e) { - std::cerr << e.what() << ", application will now exit" << std::endl; - exit(2); - } - - auto rawfilename = vm["input-file"].as(); - - o2::raw::RawFileReader reader; - reader.setDefaultDataOrigin(o2::header::gDataOriginEMC); - reader.setDefaultDataDescription(o2::header::gDataDescriptionRawData); - reader.setDefaultReadoutCardType(o2::raw::RawFileReader::RORC); - reader.addFile(rawfilename); - reader.init(); - - o2::emcal::MappingHandler mapper; - o2::emcal::TriggerMappingV2 triggermapping; - - std::unique_ptr treefile(TFile::Open("trudata.root", "RECREATE")); - TTree trudata("trudata", "Tree with TRU data"); - // branches in tree - struct collisiontrigger { - unsigned long bc; - unsigned long orbit; - } mycollision; - int absFastOR; - int starttime; - std::vector timesamples; - tree->Branch(&mycollision, "collisiontrigger", "bc,orbit/l"); - tree->Branch(&starttime, "starttime", "starttime/i"); - tree->Branch(×amples, "timesamples", ""); // @todo check how to write std::vector to tree; - - while (1) { - int tfID = reader.getNextTFToRead(); - if (tfID >= reader.getNTimeFrames()) { - LOG(info) << "nothing left to read after " << tfID << " TFs read"; - break; - } - std::vector dataBuffer; // where to put extracted data - for (int il = 0; il < reader.getNLinks(); il++) { - auto& link = reader.getLink(il); - std::cout << "Decoding link " << il << std::endl; - - auto sz = link.getNextTFSize(); // size in bytes needed for the next TF of this link - dataBuffer.resize(sz); - link.readNextTF(dataBuffer.data()); - - // Parse - o2::emcal::RawReaderMemory parser(dataBuffer); - while (parser.hasNext()) { - parser.next(); - auto rdh = parser.getRawHeader(); - auto ddl = o2::raw::RDHUtils::getFEEID(parser.getRawHeader()); - // Exclude STU DDLs - if (ddl >= 40) { - continue; - } - - mycollision.bc = o2::raw::RDHUtils::getTriggerBC(rdh); - mycollision.orbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - - o2::emcal::AltroDecoder decoder(parser); - decoder.decode(); - auto& ddlmapping = mapper.getMappingForDDL(ddl); - - std::cout << decoder.getRCUTrailer() << std::endl; - for (auto& chan : decoder.getChannels()) { - if (ddlmapping.getChannelType(chan.getHardwareAddress) != o2::emcal::ChannelType_t::TRU) { - continue; - } - std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; - // Get absolute FastOR index - this will tell us where on the EMCAL surface the FastOR is - // TRU index is encoded in column, needs to be converted to an absoluted FastOR ID via the - // trigger mapping. The absoluted FastOR ID can be connected via the geometry to tower IDs - // from the FEC data. - // we are only interested in the FastORs for now, skip patches starting from 96 - auto fastorInTRU = ddlmapping.getColumn(chan.getHardwareAddress()); - if (fastorInTRU >= 96) { - // indices starting from 96 encode patches, not FastORs - continue; - } - auto truindex = triggermapping.getTRUIndexFromOnlineHardareAddree(chan.getHardwareAddress(), ddl, ddl / 2); - auto absFastOrID = triggermapping.getAbsFastORIndexFromIndexInTRU(truindex, fastorInTRU); - - for (auto& bunch : chan.getBunches()) { - std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; - auto adcs = bunch.getADC(); - int time = bunch.getStartTime(); - starttime = time; - timesamples.clear(); - timesamples.resize(adcs.size()); - std::copy(adcs.begin(), adcs.end(), timesamples.begin()); - trudata.Fill(); - for (int i = adcs.size() - 1; i >= 0; i--) { - std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; - time--; - } - } - } - } - } - reader.setNextTFToRead(++tfID); - } -} \ No newline at end of file diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h index 0215e0ae65e43..9cc5ba7887473 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace emcal class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity, unsigned int sspecOut); + EntropyDecoderSpec(int verbosity, unsigned int sspecOut, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut = 0); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut = 0, const std::string& ctfdictOpt = "none"); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h index cdfb342e7ff11..df502beef30df 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace emcal class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx index 700f468e9e73d..ecc0e45492bea 100644 --- a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace emcal { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, unsigned int sspecOut) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder), mSSpecOut(sspecOut) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, unsigned int sspecOut, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt), mSSpecOut(sspecOut) { mTimer.Stop(); mTimer.Reset(); @@ -74,7 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "EMC", "CELLSTRGR", sspecOut, Lifetime::Timeframe}, @@ -83,17 +82,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, un std::vector inputs; inputs.emplace_back("ctf_EMC", "EMC", "CTFDATA", sspecInp, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_EMC", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_EMC", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "emcal-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity, sspecOut)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, sspecOut, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx index 773c4c65fc9fe..2928a71a167bc 100644 --- a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace emcal { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -71,12 +70,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "EMC", "CELLSTRGR", 0, Lifetime::Timeframe); inputs.emplace_back("cells", "EMC", "CELLS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -85,14 +87,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"EMC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "EMC", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{ - {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, - {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx b/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx index 73987ce6d1c1b..227fc373bf20c 100644 --- a/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx +++ b/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx @@ -17,7 +17,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "MathUtils/Utils.h" using namespace o2::framework; @@ -105,7 +104,7 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) o2::math_utils::detail::truncateFloatFraction(cell.getTimeStamp(), mCaloTime), cell.getType(), 1); // hard coded for emcal (-1 would be undefined, 0 phos) - } // end of cell loop + } // end of cell loop // filled only with BCID, rest dummy for no2 caloCellsTRGTableCursor(0, diff --git a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx index e6af02fa10d49..953b726fcb971 100644 --- a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::emcal::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::emcal::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FDD/base/src/Geometry.cxx b/Detectors/FIT/FDD/base/src/Geometry.cxx index 441a30fdda44f..149c086f4fc81 100644 --- a/Detectors/FIT/FDD/base/src/Geometry.cxx +++ b/Detectors/FIT/FDD/base/src/Geometry.cxx @@ -152,7 +152,7 @@ void Geometry::buildGeometry() if (!vCaveRB24) { LOG(fatal) << "Could not find the top volume for A-side"; } - const Float_t kPosFDA = 1696.67 - 1313.347; // z-center of assembly (cm) + const Float_t kPosFDA = 1696.67 - 1313.347 - 75.; // z-center of assembly (cm) vCaveRB24->AddNode(vFDAarray, 1, new TGeoTranslation(0., 0., kPosFDA - kFDACelldz / 2. - 0.1)); vCaveRB24->AddNode(vFDAarray, 2, new TGeoTranslation(0., 0., kPosFDA + kFDACelldz / 2. + 0.1)); diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index dc11174908c75..cb3b13aa9b8e4 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -33,10 +33,10 @@ namespace o2 namespace fdd { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FDD) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FDD, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h deleted file mode 100644 index 54c8b7b203edb..0000000000000 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ReadRaw.h -/// \brief Reads raw data and converts to digits -/// \author Maciej.Slupecki@cern.ch, arvind.khuntia@cern.ch, based on the FT0 code -// RAW data format description: DataFormat/Detectors/FIT/FDD/RawEventData - -#ifndef ALICEO2_FDD_READRAW_H_ -#define ALICEO2_FDD_READRAW_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "TBranch.h" -#include "TTree.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" -#include "DataFormatsFDD/LookUpTable.h" -#include "DataFormatsFDD/RawEventData.h" - -namespace o2 -{ -namespace fdd -{ -class ReadRaw -{ - public: - ReadRaw() = default; - ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath = "fdd.raw", const std::string outputRawFilePath = "fdddigitsFromRaw.root"); - void readRawData(const LookUpTable& lut); - void writeDigits(const std::string& outputDigitsFilePath); - void close(); - - private: - std::ifstream mRawFileIn; - std::map> mDigitAccum; // digit accumulator - - template - TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr) - { - if (auto br = tree.GetBranch(brname.c_str())) { - br->SetAddress(static_cast(ptr)); - return br; - } - // otherwise make it - return tree.Branch(brname.c_str(), ptr); - } - - ClassDefNV(ReadRaw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h index 161b800a2c3ca..8881605b652ac 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h @@ -17,6 +17,7 @@ #include #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" namespace o2 { namespace fdd @@ -30,10 +31,16 @@ class Reconstructor gsl::span inChData, std::vector& RecPoints, std::vector& outChData); - void finish(); + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) + { + LOG(info) << "Updated dead channel map"; + mDeadChannelMap = deadChannelMap; + } + private: + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(Reconstructor, 3); }; } // namespace fdd diff --git a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx index 3a87a11046a77..7d133e30df08e 100644 --- a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx +++ b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx @@ -33,7 +33,12 @@ void Reconstructor::process(o2::fdd::Digit const& digitBC, gsl::spanisChannelAlive(inChData[ich].mPMNumber)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } bool inTime = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsEventInTVDC); bool inAdcGate = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsCFDinADCgate); if (inAdcGate) { diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h deleted file mode 100644 index 4afcf5da37ae8..0000000000000 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file Digits2Raw.h -/// \brief converts digits to raw format -/// \author Maciej.Slupecki@cern.ch -// based on FV0 - -#ifndef ALICEO2_FDD_DIGITS2RAW_H_ -#define ALICEO2_FDD_DIGITS2RAW_H_ - -#include "Headers/RAWDataHeader.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFDD/RawEventData.h" -#include "DataFormatsFDD/LookUpTable.h" -#include "DataFormatsFDD/ChannelData.h" -#include "DataFormatsFDD/Digit.h" -#include "DetectorsRaw/HBFUtils.h" -#include "DetectorsRaw/RawFileWriter.h" -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace fdd -{ -class Digits2Raw -{ - public: - Digits2Raw() = default; - void readDigits(const std::string& outDir, const std::string& fileDigitsName); - void convertDigits(o2::fdd::Digit bcdigits, - gsl::span pmchannels, - const o2::fdd::LookUpTable& lut); - - o2::raw::RawFileWriter& getWriter() { return mWriter; } - void setFilePerLink(bool v) { mOutputPerLink = v; } - bool getFilePerLink() const { return mOutputPerLink; } - - int carryOverMethod(const header::RDHAny* rdh, const gsl::span data, - const char* ptr, int maxSize, int splitID, - std::vector& trailer, std::vector& header) const; - - private: - static constexpr uint32_t sTcmLink = 2; - static constexpr uint16_t sCruId = 0; - static constexpr uint32_t sEndPointId = sCruId; - - void makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord); - void fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir); - RawEventData mRawEventData; - o2::fdd::Triggers mTriggers; - o2::raw::RawFileWriter mWriter{"FDD"}; - bool mOutputPerLink = false; - ///////////////////////////////////////////////// - - ClassDefNV(Digits2Raw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h index a6ee132ee0c34..1fd3cd7835cd9 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace fdd class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h index 87dcca02e869f..37d43f477e836 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace fdd class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h deleted file mode 100644 index 6ed465b6181dd..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.h - -#ifndef O2_FDD_RAWDATAPROCESSSPEC_H -#define O2_FDD_RAWDATAPROCESSSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FDDRaw/DigitBlockFDD.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -class RawDataProcessSpec : public Task -{ - public: - RawDataProcessSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~RawDataProcessSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; -}; - -framework::DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor); - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h deleted file mode 100644 index c3b0349826e98..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataReaderSpec.h - -#ifndef O2_FDD_RAWDATAREADERSPEC_H -#define O2_FDD_RAWDATAREADERSPEC_H - -#include "DataFormatsFDD/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "DetectorsRaw/RDHUtils.h" - -#include -#include -#include -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -template -class RawDataReaderSpec : public Task -{ - public: - RawDataReaderSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - RawDataReaderSpec() = default; - ~RawDataReaderSpec() override = default; - void init(InitContext& ic) final { o2::fdd::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - DPLRawParser parser(pc.inputs()); - mRawReader.clear(); - LOG(info) << "FDD RawDataReaderSpec"; - uint64_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), int(0)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - } - RawReader mRawReader; -}; - -template -framework::DataProcessorSpec getFDDRawDataReaderSpec(const RawReader& rawReader) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFDD"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - return DataProcessorSpec{ - "fdd-datareader-dpl", - o2::framework::select("TF:FDD/RAWDATA"), - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAREADERDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h index 2dbd854e34eee..0d5d308216bb0 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fdd } // namespace o2 #endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h index 7dcb5d9aaba40..8f20ff1513ab4 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h @@ -18,6 +18,8 @@ #include "Framework/Task.h" #include "FDDReconstruction/Reconstructor.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" +#include "Framework/ConcreteDataMatcher.h" using namespace o2::framework; @@ -29,21 +31,25 @@ namespace fdd class FDDReconstructorDPL : public Task { public: - FDDReconstructorDPL(bool useMC) : mUseMC(useMC) {} + FDDReconstructorDPL(bool useMC, bool useDeadChannelMap) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap) {} ~FDDReconstructorDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: bool mUseMC = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; std::vector mRecPoints; std::vector mRecChData; + o2::fit::DeadChannelMap const* mDeadChannelMap; o2::fdd::Reconstructor mReco; o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; }; /// create a processor spec -framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true); +framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true, bool useDeadChannelMap = true); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx index fb5b173fb7a94..33c140b5bc198 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace fdd { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -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"}, "FDD", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -82,17 +81,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_FDD", "FDD", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_FDD", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FDD", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "fdd-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"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 fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx index abb2518e5ae0b..be81f7ca7d3d4 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace fdd { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -69,12 +68,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FDD", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FDD", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -82,13 +84,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) "fdd-entropy-encoder", inputs, Outputs{{"FDD", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx deleted file mode 100644 index bf18db67672c2..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.cxx - -#include "FDDWorkflow/RawDataProcessSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -using namespace std; -void RawDataProcessSpec::init(InitContext& ic) -{ -} - -void RawDataProcessSpec::run(ProcessingContext& pc) -{ - LOG(info) << "RawDataProcessSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFDD::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getRawDataProcessSpec"; - return DataProcessorSpec{ - "fdd-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx deleted file mode 100644 index c9e5c5be0c81d..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawWorkflow.cxx - -#include "FDDWorkflow/RawWorkflow.h" -#include "FDDWorkflow/RawDataProcessSpec.h" -#include "FDDWorkflow/RawDataReaderSpec.h" -#include "FDDWorkflow/DigitWriterSpec.h" -#include "FDDWorkflow/RawReaderFDD.h" -namespace o2 -{ -namespace fdd -{ - -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut) -{ - LOG(info) << "framework::WorkflowSpec getFDDWorkflow"; - framework::WorkflowSpec specs; - specs.emplace_back(o2::fdd::getFDDRawDataReaderSpec(RawReaderFDD{dumpReader})); - - if (useProcess) { - specs.emplace_back(o2::fdd::getFDDRawDataProcessSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::fdd::getFDDDigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx index a7d4c15af81bb..b464e689f7a75 100644 --- a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,14 @@ namespace o2 namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fdd::getFDDDigitReaderSpec(useMC)); } - specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC)); + specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fdd::getFDDRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx index b7a0b9876a2ee..1d5d599b5ee31 100644 --- a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx @@ -18,6 +18,7 @@ #include "FDDWorkflow/ReconstructorSpec.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" +#include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -44,6 +45,11 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) // lblPtr = labels.get(); LOG(info) << "Ignoring MC info"; } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(info) << "Populating reconsturctor object with Dead Channel Map object"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.setDeadChannelMap(deadChannelMap.get()); + } int nDig = digitsBC.size(); mRecPoints.reserve(nDig); mRecChData.reserve(digitsCh.size()); @@ -58,16 +64,29 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); } -DataProcessorSpec getFDDReconstructorSpec(bool useMC) +void FDDReconstructorDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("FDD", "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + return; + } +} + +DataProcessorSpec getFDDReconstructorSpec(bool useMC, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digitsBC", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digitsCh", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently FDDReconstructor does not consume and provide MC truth"; // inputSpec.emplace_back("labels", o2::header::gDataOriginFDD, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFDD, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/DeadChannelMap")); + } outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECCHDATA", 0, Lifetime::Timeframe); @@ -75,7 +94,7 @@ DataProcessorSpec getFDDReconstructorSpec(bool useMC) "fdd-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx index bcc42ebc2e086..0e43c6e3c4ba0 100644 --- a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::fdd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::fdd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx index 652ddb8bd2a29..888792425909b 100644 --- a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx @@ -38,6 +38,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -57,8 +58,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); - auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FT0/calibration/CMakeLists.txt b/Detectors/FIT/FT0/calibration/CMakeLists.txt index d103b4a9a18b6..bee0493d300c1 100644 --- a/Detectors/FIT/FT0/calibration/CMakeLists.txt +++ b/Detectors/FIT/FT0/calibration/CMakeLists.txt @@ -10,26 +10,50 @@ # or submit itself to any jurisdiction. o2_add_library(FT0Calibration - SOURCES - src/FT0TimeOffsetSlotContainer.cxx - PUBLIC_LINK_LIBRARIES - O2::DataFormatsFT0 - O2::CommonDataFormat - O2::DetectorsCalibration - ) - o2_target_root_dictionary(FT0Calibration - HEADERS - include/FT0Calibration/FT0TimeOffsetSlotContainer.h - ) - o2_add_executable(ft0-time-offset-calib - COMPONENT_NAME calibration - SOURCES workflow/FT0TimeOffsetCalibration-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-time-spectra-processor - COMPONENT_NAME calibration - SOURCES workflow/FT0TimeSpectraProcessor-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) + SOURCES + src/FT0TimeOffsetSlotContainer.cxx + src/EventsPerBcCalibrator.cxx + PUBLIC_LINK_LIBRARIES + O2::DetectorsCalibration + O2::Framework + O2::CommonUtils + Microsoft.GSL::GSL + O2::DataFormatsFT0 + O2::CommonDataFormat + O2::Steer + O2::CCDB + ROOT::Minuit + ROOT::Hist + ) + +o2_target_root_dictionary(FT0Calibration + HEADERS + include/FT0Calibration/FT0TimeOffsetSlotContainer.h + include/FT0Calibration/EventsPerBcCalibrator.h + ) + +o2_add_executable(ft0-time-offset-calib + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeOffsetCalibration-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration O2::FITCalibration + ) + +o2_add_executable(ft0-time-spectra-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeSpectraProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + ) + +o2_add_executable(ft0-events-per-bc-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0EventsPerBcProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + O2::Framework + O2::CCDB +) \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/README.md b/Detectors/FIT/FT0/calibration/README.md new file mode 100644 index 0000000000000..78b0f980400d2 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/README.md @@ -0,0 +1,62 @@ +# Calibrations + +## Events per BC Calibration +### Description +Generates histograms of **Events per Bunch Crossing (BC)**. Events can be filtered by applying amplitude thresholds to the **A-side** and **C-side**. + +### Command-Line Options +| Option | Default | Description | +| :--- | :--- | :--- | +| `--slot-len-sec` | `3600` | Duration of each slot in seconds. | +| `--slot-len-tf` | `0` | Slot length in Time Frames (TFs). | +| `--one-object-per-run` | — | If set, the workflow creates only one calibration object per run. | +| `--min-entries-number` | `0` | Minimum number of entries required for a slot to be valid. | +| `--min-ampl-side-a` | `-2147483648` | Amplitude threshold for Side A events. | +| `--min-ampl-side-c` | `-2147483648` | Amplitude threshold for Side C events. | + +--- + +## How to Run + +### Simulation Data +First, it is important to digitize data with a non-zero run number, orbit, and timestamp. To set these parameters, one can use the `--configKeyValues` option, as shown in the example below. +``` +o2-sim-digitizer-workflow \ +--onlyDet FT0 \ +--configKeyValues="HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=128;HBFUtils.orbitFirstSampled=256;HBFUtils.runNumber=560560;HBFUtils.startTime=1768464099000" +``` + +To process simulation data, digits must first be converted to RAW format. The `o2-ft0-digi2raw` tool performs this conversion and generates the required configuration file. + +Once converted, you can run the calibration either as a single integrated workflow or by spawning as the sender and receiver components separately. + +#### Single Workflow Example +Execute the following command within the simulation directory: +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080 +``` + +Sender example (in simulation directory): +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-dpl-output-proxy --channel-config "name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq" --dataspec "downstream:FT0/DIGITSBC" +``` + +Receiver example: +``` +o2-dpl-raw-proxy --channel-config "name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" --dataspec "A:FT0/DIGITSBC/0" \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10 --min-ampl-side-a=0" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` + +### CTF Data +Example: +``` +o2-ctf-reader-workflow --ctf-input ctf.root --onlyDet FT0 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h new file mode 100644 index 0000000000000..d831cc36201ab --- /dev/null +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FT0TVXPERBCID +#define O2_FT0TVXPERBCID + +#include +#include +#include +#include + +#include "CommonDataFormat/FlatHisto2D.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "CommonDataFormat/TFIDInfo.h" +#include "TH1F.h" +#include "Rtypes.h" + +namespace o2::ft0 +{ +struct EventsPerBcContainer { + EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) {} + + size_t getEntries() const { return entries; } + void print() const; + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data); + void merge(const EventsPerBcContainer* prev); + + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::array mTvx{0.0}; + size_t entries{0}; + long startTimeStamp{0}; + long stopTimeStamp{0}; + + ClassDefNV(EventsPerBcContainer, 1); +}; + +class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration +{ + using Slot = o2::calibration::TimeSlot; + using TFType = o2::calibration::TFType; + using EventsHistogram = std::array; + + public: + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude); + + bool hasEnoughData(const Slot& slot) const override; + void initOutput() override; + void finalizeSlot(Slot& slot) override; + Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) override; + + const std::vector& getTvxPerBc() { return mTvxPerBcs; } + std::vector>& getTvxPerBcCcdbInfo() { return mTvxPerBcInfos; } + + private: + const uint32_t mMinNumberOfEntries; + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::vector mTvxPerBcs; + std::vector> mTvxPerBcInfos; + + ClassDefOverride(EventsPerBcCalibrator, 1); +}; +} // namespace o2::ft0 + +#endif diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx new file mode 100644 index 0000000000000..b17c81213cd08 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0Calibration/EventsPerBcCalibrator.h" +#include "CommonUtils/MemFileHelper.h" + +namespace o2::ft0 +{ +void EventsPerBcContainer::print() const +{ + LOG(info) << entries << " entries"; +} + +void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data) +{ + size_t oldEntries = entries; + for (const auto& digit : data) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC && (digit.mTriggers.getAmplA() + digit.mTriggers.getAmplC()) >= mMinSumOfAmplitude) { + mTvx[digit.mIntRecord.bc]++; + entries++; + } + } + LOG(debug) << "Container is filled with " << entries - oldEntries << " new events"; +} + +void EventsPerBcContainer::merge(const EventsPerBcContainer* prev) +{ + for (int bc = 0; bc < o2::constants::lhc::LHCMaxBunches; bc++) { + mTvx[bc] += prev->mTvx[bc]; + } + entries += prev->entries; +} + +void EventsPerBcCalibrator::initOutput() +{ + mTvxPerBcs.clear(); + mTvxPerBcInfos.clear(); +} + +EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) +{ + LOG(info) << "Defined threshold for number of entires per slot: " << mMinNumberOfEntries; + LOG(info) << "Defined threshold for side A amplitude for event: " << mMinAmplitudeSideA; + LOG(info) << "Defined threshold for side C amplitude for event: " << mMinAmplitudeSideC; +} + +bool EventsPerBcCalibrator::hasEnoughData(const EventsPerBcCalibrator::Slot& slot) const +{ + return slot.getContainer()->entries > mMinNumberOfEntries; +} + +void EventsPerBcCalibrator::finalizeSlot(EventsPerBcCalibrator::Slot& slot) +{ + LOG(info) << "Finalizing slot from " << slot.getStartTimeMS() << " to " << slot.getEndTimeMS(); + o2::ft0::EventsPerBcContainer* data = slot.getContainer(); + mTvxPerBcs.emplace_back(data->mTvx); + + auto clName = o2::utils::MemFileHelper::getClassName(mTvxPerBcs.back()); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + + std::map metaData; + mTvxPerBcInfos.emplace_back(std::make_unique("FT0/Calib/EventsPerBc", clName, flName, metaData, slot.getStartTimeMS(), slot.getEndTimeMS())); + LOG(info) << "Created object valid from " << mTvxPerBcInfos.back()->getStartValidityTimestamp() << " to " << mTvxPerBcInfos.back()->getEndValidityTimestamp(); +} + +EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, TFType tstart, TFType tend) +{ + auto& cont = getSlots(); + auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); + slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude)); + return slot; +} +} // namespace o2::ft0 \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h index 49f72e8cbdfff..11b1ce25e9353 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h +++ b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h @@ -16,7 +16,9 @@ #pragma link off all functions; #pragma link C++ class o2::ft0::FT0TimeOffsetSlotContainer + ; +#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::EventsPerBcContainer> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::EventsPerBcContainer> + ; #endif 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/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 9621d1a079bc9..16476ae3b8ccc 100644 --- a/Detectors/FIT/FT0/macros/FT0Misaligner.C +++ b/Detectors/FIT/FT0/macros/FT0Misaligner.C @@ -55,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); 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..5c2e0f0627ef1 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(), 1.f, o2::detectors::DetID::FT0, 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 7363cef57cf31..3e3ffe52671e9 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -67,6 +67,10 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, // 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); 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/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/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/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..2b4a86df0a614 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, "ccdb", {"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/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 88f7a0b82b8b3..61be50b48dede 100644 --- a/Detectors/FIT/FV0/macros/FV0Misaligner.C +++ b/Detectors/FIT/FV0/macros/FV0Misaligner.C @@ -54,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); 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..fdff035b934ef 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(), 1.f, o2::detectors::DetID::FV0, 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/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/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..f1b1bfa456316 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, "ccdb", {"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/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/FOCAL/simulation/data/simcuts.dat b/Detectors/FOCAL/simulation/data/simcuts.dat index 744f67c3c81f4..870e38182f01c 100644 --- a/Detectors/FOCAL/simulation/data/simcuts.dat +++ b/Detectors/FOCAL/simulation/data/simcuts.dat @@ -14,3 +14,5 @@ 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. - FOC 6 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 * Aluminium FOC 11 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Air +FOC 13 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 diff --git a/Detectors/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/GRP/calibration/src/GRPDCSDPsProcessor.cxx b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx index c8fa7c2bff38b..aec4241f4f8db 100644 --- a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx +++ b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx @@ -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; } diff --git a/Detectors/GRP/workflows/src/create-grp-ecs.cxx b/Detectors/GRP/workflows/src/create-grp-ecs.cxx index 873133e0dd46b..d9a73f0737799 100644 --- a/Detectors/GRP/workflows/src/create-grp-ecs.cxx +++ b/Detectors/GRP/workflows/src/create-grp-ecs.cxx @@ -268,10 +268,10 @@ 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"); @@ -279,7 +279,7 @@ int main(int argc, char** argv) 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"); @@ -313,7 +313,7 @@ int main(int argc, char** argv) 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") { diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index 89d6f8347373d..6a3486dd12044 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -1581,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++) { @@ -1598,6 +1600,10 @@ 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; @@ -1702,8 +1708,8 @@ void MatchTOF::BestMatches(std::vector& match matchingPair.setT0true(TOFClusWork[matchingPair.getTOFClIndex()].getT0true()); // let's check if cluster has multiple-hits (noferini) - if (TOFClusWork[matchingPair.getTOFClIndex()].getNumOfContributingChannels() > 1) { - const auto& tofcl = TOFClusWork[matchingPair.getTOFClIndex()]; + 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) || @@ -1719,6 +1725,19 @@ void MatchTOF::BestMatches(std::vector& match 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 diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index c8c9dda6a4025..5f99ad2202073 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#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" #ifdef WITH_OPENMP @@ -101,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.); } @@ -245,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(); @@ -1754,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); @@ -1789,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; 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/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/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 776d3946283c3..df42af503db46 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -25,6 +25,8 @@ o2_add_library(GlobalTrackingStudy src/TrackMCStudyConfig.cxx src/TrackMCStudyTypes.cxx src/TPCClusSelector.cxx + src/CheckResid.cxx + src/CheckResidConfig.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers @@ -38,6 +40,8 @@ o2_target_root_dictionary(GlobalTrackingStudy include/GlobalTrackingStudy/TrackInfoExt.h include/GlobalTrackingStudy/TrackMCStudyConfig.h include/GlobalTrackingStudy/TrackMCStudyTypes.h + include/GlobalTrackingStudy/CheckResidTypes.h + include/GlobalTrackingStudy/CheckResidConfig.h LINKDEF src/GlobalTrackingStudyLinkDef.h ) @@ -76,6 +80,11 @@ o2_add_executable(dump-workfow 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) diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h similarity index 53% rename from Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h rename to Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h index 4d104aacac15c..a78fa5e8d41da 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h @@ -9,22 +9,19 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file MIDWorkflow/RawDumpSpec.h -/// \brief Device to dump decoded raw data -/// \author Diego Stocco -/// \date 17 February 2022 - -#ifndef O2_MID_RAWDUMPSPEC_H -#define O2_MID_RAWDUMPSPEC_H +#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 -{ -namespace mid +namespace o2::checkresid { -framework::DataProcessorSpec getRawDumpSpec(); -} // namespace mid -} // namespace o2 +/// 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_MID_RAWDUMPSPEC_H +#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/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h index 2eed64df3bfca..e33a0def63842 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -53,7 +53,7 @@ struct TrackInfoExt { 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)); } @@ -74,7 +74,7 @@ struct TrackInfoExt { void resetBit(int bit) { flags &= ~(kBitMask & (0x1 << bit)); } bool isBitSet(int bit) const { return flags & (kBitMask & (0x1 << bit)); } - ClassDefNV(TrackInfoExt, 7); + ClassDefNV(TrackInfoExt, 8); }; } // namespace dataformats diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h index 7d89928a20b37..ed78ba2a710ec 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h @@ -27,9 +27,14 @@ struct TrackMCStudyConfig : o2::conf::ConfigurableParamHelper #include @@ -32,7 +33,8 @@ struct MCTrackInfo { 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; @@ -52,7 +54,28 @@ struct MCTrackInfo { uint8_t maxTPCRowSect = -1; int8_t nITSCl = 0; int8_t pattITSCl = 0; - ClassDefNV(MCTrackInfo, 4); + 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 { @@ -63,6 +86,7 @@ struct RecTrack { FakeTOF = 0x1 << 3, FakeITSTPC = 0x1 << 4, FakeITSTPCTRD = 0x1 << 5, + HASACSides = 0x1 << 6, FakeGLO = 0x1 << 7 }; o2::track::TrackParCov track{}; @@ -70,11 +94,15 @@ struct RecTrack { 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; } @@ -82,8 +110,9 @@ struct RecTrack { 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, 1); + ClassDefNV(RecTrack, 3); }; struct TrackPairInfo { @@ -133,6 +162,13 @@ struct TrackFamily { // set of tracks related to the same MC label 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); @@ -254,6 +290,16 @@ struct ClResTPC { 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{}; @@ -270,7 +316,8 @@ struct MCVertex { int nTrackSel = 0; // number of selected MC charged tracks int ID = -1; std::vector recVtx{}; - ClassDefNV(MCVertex, 1); + std::vector occTPCV{}; + ClassDefNV(MCVertex, 2); }; } // namespace o2::trackstudy 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/Framework/TestWorkflows/src/dummy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx similarity index 84% rename from Framework/TestWorkflows/src/dummy.cxx rename to Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx index faff430964e73..a754d1196017f 100644 --- a/Framework/TestWorkflows/src/dummy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx @@ -8,3 +8,7 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. + +#include "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 f0d3e7d4d0b4e..416820fc9aebb 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -40,4 +40,13 @@ #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 1e141a29d3f55..0129d19b02346 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -68,7 +68,7 @@ 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 useTPCCl, bool useMC) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index 1cb108da5a460..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; diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index d380a4f05cedf..8f6604b029605 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -19,11 +19,14 @@ #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "ITSMFTReconstruction/ChipMappingITS.h" +#include "ITStracking/IOUtils.h" #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" +#include "ITSBase/GeometryTGeo.h" #include "SimulationDataFormat/MCEventLabel.h" #include "SimulationDataFormat/MCUtils.h" #include "SimulationDataFormat/O2DatabasePDG.h" +#include "SimulationDataFormat/TrackReference.h" #include "CommonDataFormat/BunchFilling.h" #include "CommonUtils/NameConf.h" #include "DataFormatsFT0/RecPoints.h" @@ -80,7 +83,7 @@ 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, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) @@ -88,6 +91,7 @@ class TrackMCStudy : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TrackMCStudy() final = default; void init(InitContext& ic) final; @@ -98,6 +102,7 @@ class TrackMCStudy : public Task private: void processTPCTrackRefs(); + void processITSTracks(const o2::globaltracking::RecoContainer& recoData); void loadTPCOccMap(const o2::globaltracking::RecoContainer& recoData); void fillMCClusterInfo(const o2::globaltracking::RecoContainer& recoData); void prepareITSData(const o2::globaltracking::RecoContainer& recoData); @@ -109,7 +114,7 @@ class TrackMCStudy : public Task void updateTimeDependentParams(ProcessingContext& pc); float getDCAYCut(float pt) const; - gsl::span mCurrMCTracks; + const std::vector* mCurrMCTracks = nullptr; TVector3 mCurrMCVertex; o2::tpc::VDriftHelper mTPCVDriftHelper{}; o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; @@ -121,9 +126,13 @@ class TrackMCStudy : public Task 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; + float mNTPCOccBinLengthInv = -1.f; int mVerbose = 0; float mITSTimeBiasMUS = 0.f; float mITSROFrameLengthMUS = 0.f; ///< ITS RO frame in mus @@ -181,10 +190,11 @@ void TrackMCStudy::run(ProcessingContext& pc) } mDecProdLblPool.clear(); mMCVtVec.clear(); - mCurrMCTracks = {}; + 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); } @@ -218,7 +228,7 @@ 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()); @@ -278,15 +288,34 @@ void TrackMCStudy::process(const 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; }; auto flagTPCClusters = [&recoData](const o2::tpc::TrackTPC& trc, o2::MCCompLabel lbTrc) { @@ -338,6 +367,21 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) } 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 } @@ -352,15 +396,17 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) int nev = mcReader.getNEvents(curSrcMC); bool okAccVtx = true; if (nev != (int)mMCVtVec.size()) { - LOGP(error, "source {} has {} events while {} MC vertices were booked", curSrcMC, nev, 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); } - const auto& mt = mcReader.getTracks(curSrcMC, curEvMC); - mCurrMCTracks = gsl::span(mt.data(), mt.size()); + mCurrMCTracks = &mcReader.getTracks(curSrcMC, curEvMC); const_cast(mcReader.getMCEventHeader(curSrcMC, curEvMC)).GetVertex(mCurrMCVertex); if (okAccVtx) { auto& pos = mMCVtVec[curEvMC].pos; @@ -370,7 +416,7 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) pos[2] = mCurrMCVertex.Z(); } } - for (int itr = 0; itr < mCurrMCTracks.size(); itr++) { + for (int itr = 0; itr < mCurrMCTracks->size(); itr++) { processMCParticle(curSrcMC, curEvMC, itr); } } @@ -382,6 +428,7 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) } // add reconstruction info to MC particles. If MC particle was not selected before but was reconstrected, account MC info + mRecProcStage = true; // MC particles accepted only at this stage will be flagged for (int iv = 0; iv < nv; iv++) { if (mVerbose > 1) { LOGP(info, "processing PV {} of {}", iv, nv); @@ -416,11 +463,10 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) if (lbl.getSourceID() != curSrcMC || lbl.getEventID() != curEvMC) { curSrcMC = lbl.getSourceID(); curEvMC = lbl.getEventID(); - const auto& mt = mcReader.getTracks(curSrcMC, curEvMC); - mCurrMCTracks = gsl::span(mt.data(), mt.size()); + mCurrMCTracks = &mcReader.getTracks(curSrcMC, curEvMC); const_cast(mcReader.getMCEventHeader(curSrcMC, curEvMC)).GetVertex(mCurrMCVertex); } - if (!acceptMCCharged(mCurrMCTracks[lbl.getTrackID()], lbl)) { + if (!acceptMCCharged((*mCurrMCTracks)[lbl.getTrackID()], lbl)) { continue; } entry = mSelMCTracks.find(lbl); @@ -468,7 +514,7 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) } LOGP(info, "collected {} MC tracks", mSelMCTracks.size()); - if (params.minTPCRefsToExtractClRes > 0) { // prepare MC trackrefs for TPC + if (params.minTPCRefsToExtractClRes > 0 || params.storeTPCTrackRefs) { // prepare MC trackrefs for TPC processTPCTrackRefs(); } @@ -493,6 +539,15 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) } 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) { @@ -522,17 +577,39 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) tref.flags |= RecTrack::FakeITS; } } - if (msk[DetID::TPC] && trackFam.entITSTPC < 0) { // has both ITS and TPC contribution - trackFam.entITSTPC = tcnt; + if (msk[DetID::TPC]) { + if (trackFam.entITSTPC < 0) { // has both ITS and TPC contribution + trackFam.entITSTPC = tcnt; + } if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]).isFake()) { tref.flags |= RecTrack::FakeITSTPC; } + + if (msk[DetID::TRD]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTRD]).isFake()) { + tref.flags |= RecTrack::FakeTRD; + } + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTRDTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } else { + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } } } if (msk[DetID::TPC]) { const auto& trtpc = recoData.getTPCTrack(gidSet[GTrackID::TPC]); tref.nClTPC = trtpc.getNClusters(); - tref.lowestPadRow = getLowestPadrow(trtpc); + if (trtpc.hasBothSidesClusters()) { + tref.flags |= RecTrack::HASACSides; + } + fillTPCClusterInfo(trtpc, tref); flagTPCClusters(trtpc, entry.first); if (trackFam.entTPC < 0) { trackFam.entTPC = tcnt; @@ -541,6 +618,24 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) 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) { @@ -556,8 +651,8 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) tcnt++; } if (trackFam.entITS > -1 && trackFam.entTPC > -1) { // ITS and TPC were found but matching failed - auto vidITS = tracks[trackFam.entITS].gid; - auto vidTPC = tracks[trackFam.entTPC].gid; + 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)) { @@ -663,6 +758,10 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) }); (*mDBGOut) << "mcVtxTree" << "mcVtx=" << mcVtx << "\n"; } + + if (params.storeITSInfo) { + processITSTracks(recoData); + } } void TrackMCStudy::processTPCTrackRefs() @@ -723,8 +822,8 @@ void TrackMCStudy::fillMCClusterInfo(const o2::globaltracking::RecoContainer& re const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); ClResTPC clRes{}; - for (uint8_t sector = 0; sector < 36; sector++) { - for (uint8_t row = 0; row < 152; row++) { + 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); @@ -935,6 +1034,11 @@ 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; + } } //_____________________________________________________ @@ -968,7 +1072,7 @@ float TrackMCStudy::getDCAYCut(float pt) const bool TrackMCStudy::processMCParticle(int src, int ev, int trid) { - const auto& mcPart = mCurrMCTracks[trid]; + const auto& mcPart = (*mCurrMCTracks)[trid]; int pdg = mcPart.GetPdgCode(); bool res = false; while (true) { @@ -990,7 +1094,7 @@ bool TrackMCStudy::processMCParticle(int src, int ev, int trid) break; } for (int idd = idd0; idd <= idd1; idd++) { - const auto& product = mCurrMCTracks[idd]; + const auto& product = (*mCurrMCTracks)[idd]; auto lbld = o2::MCCompLabel(idd, ev, src); if (!acceptMCCharged(product, lbld, decay)) { decay = -1; // discard decay @@ -1088,10 +1192,17 @@ bool TrackMCStudy::addMCParticle(const MCTrack& mcPart, const o2::MCCompLabel& l 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]; + const auto& mcPartPar = (*mCurrMCTracks)[moth]; mcEntry.mcTrackInfo.pdgParent = mcPartPar.GetPdgCode(); } if (mcPart.isPrimary() && mcReader.getNEvents(lb.getSourceID()) == mMCVtVec.size()) { @@ -1181,6 +1292,85 @@ void TrackMCStudy::loadTPCOccMap(const o2::globaltracking::RecoContainer& recoDa } } +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; diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx index 204e0c741a675..b6236b7bf0e73 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx @@ -77,4 +77,66 @@ int MCTrackInfo::getHighestITSLayer() const 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 97721a30b0ab8..b8a8f97737b4d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -44,7 +44,7 @@ #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" @@ -67,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) @@ -75,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; @@ -274,25 +275,25 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) const auto clRefs = recoData.getTPCTracksClusterRefs(); const auto tpcClusAcc = recoData.getTPCClusters(); const auto shMap = recoData.clusterShMapTPC; + if (recoData.inputsTPCclusters) { - uint8_t clSect = 0, clRow = 0, clRowP = -1; + uint8_t clSect = 0, clRow = 0, lowestR = -1; uint32_t clIdx = 0; - for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { + 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 != clRowP) { + if (clRow < lowestR) { trExt.rowCountTPC++; - clRowP = clRow; + lowestR = clRow; } unsigned int absoluteIndex = tpcClusAcc.clusterOffset[clSect][clRow] + clIdx; if (shMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { trExt.nClTPCShared++; } } - trc.getClusterReference(clRefs, trc.getNClusterReferences() - 1, clSect, clRow, clIdx); - trExt.rowMinTPC = clRow; + trExt.rowMinTPC = lowestR; const auto& clus = tpcClusAcc.clusters[clSect][clRow][clIdx]; trExt.padFromEdge = uint8_t(clus.getPad()); - int npads = o2::gpu::GPUTPCGeometry::NPads(clRow); + int npads = o2::gpu::GPUTPCGeometry::NPads(lowestR); if (trExt.padFromEdge > npads / 2) { trExt.padFromEdge = npads - 1 - trExt.padFromEdge; } @@ -314,9 +315,9 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) 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 + 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 + 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; @@ -443,7 +444,7 @@ 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 @@ -479,6 +480,7 @@ 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; 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/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..0e6694d2353ac 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(), 1.f, o2::detectors::DetID::HMP, 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/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..76e7eae10508e 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, "ccdb", {"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 fcdc978fa64f0..e236c898851f5 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 @@ -333,13 +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; /// 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 @@ -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 89b4d63729543..60570b2f204c5 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -899,19 +899,6 @@ TGeoHMatrix& GeometryTGeo::createT2LMatrix(int isn) return t2l; } -//__________________________________________________________________________ -const o2::math_utils::Transform3D GeometryTGeo::getT2LMatrixITS3(int isn, float alpha) -{ - // create for sensor isn the TGeo matrix for Tracking to Local frame transformations with correction for effective thickness - 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); -} - //__________________________________________________________________________ int GeometryTGeo::extractVolumeCopy(const char* name, const char* prefix) const { 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/CheckTracksCA.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C index 7c128ce34d538..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", @@ -124,7 +146,10 @@ void CheckTracksCA(bool doFakeClStud = false, 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(); @@ -134,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; @@ -214,117 +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"); - TCanvas* c2 = new TCanvas; - c2->SetGridx(); - c2->SetGridy(); - hZvertex->DrawClone(); - - 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; @@ -364,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) { @@ -409,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/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/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/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/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/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/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/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 2304a9102092a..8cfe13097d581 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -477,10 +477,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.}; 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 d3871b9e75d70..001ee537f50d2 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -9,16 +9,13 @@ # 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(ITStracking TARGETVARNAME targetName SOURCES src/ClusterLines.cxx src/Cluster.cxx src/Configuration.cxx - src/ROframe.cxx src/TimeFrame.cxx src/IOUtils.cxx - src/Label.cxx src/Tracker.cxx src/TrackerTraits.cxx src/TrackingConfigParam.cxx @@ -36,7 +33,10 @@ o2_add_library(ITStracking O2::ITSReconstruction O2::ITSMFTReconstruction O2::DataFormatsITS - PRIVATE_LINK_LIBRARIES TBB::tbb) + 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 @@ -56,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/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index 82101dba4c02d..d6d87eb8c1143 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -13,49 +13,55 @@ #ifndef TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H #define TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H +#include +#include + #include "ITStracking/BoundedAllocator.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/Configuration.h" #include "ITStrackingGPU/Utils.h" -#include - namespace o2::its::gpu { -class Stream; - -class DefaultGPUAllocator : public ExternalAllocator -{ - void* allocate(size_t size) override; -}; - template -class TimeFrameGPU : public TimeFrame +class TimeFrameGPU final : public TimeFrame { + using typename TimeFrame::CellSeedN; + using typename TimeFrame::IndexTableUtilsN; + public: - TimeFrameGPU(); - ~TimeFrameGPU(); + TimeFrameGPU() = default; + ~TimeFrameGPU() = default; /// Most relevant operations + void pushMemoryStack(const int); + void popMemoryStack(const int); void registerHostMemory(const int); void unregisterHostMemory(const int); - void initialise(const int, const TrackingParameters&, const int, IndexTableUtils* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); - void initDevice(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 loadIndexTableUtils(const int); - void loadTrackingFrameInfoDevice(const int); - void loadUnsortedClustersDevice(const int); - void loadClustersDevice(const int); - void loadClustersIndexTables(const int iteration); - void createUsedClustersDevice(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); + void loadROFrameClustersDevice(const int, const int); + void createROFrameClustersDeviceArray(const int); void loadMultiplicityCutMask(const int); void loadVertices(const int); /// - void createTrackletsLUTDevice(const int); + void createTrackletsLUTDevice(const int, const int); + void createTrackletsLUTDeviceArray(const int); void loadTrackletsDevice(); void loadTrackletsLUTDevice(); void loadCellsDevice(); @@ -63,33 +69,47 @@ class TimeFrameGPU : public TimeFrame void loadTrackSeedsDevice(); void loadTrackSeedsChi2Device(); void loadRoadsDevice(); - void loadTrackSeedsDevice(bounded_vector&); - void createTrackletsBuffers(); + void loadTrackSeedsDevice(bounded_vector&); + void createTrackletsBuffers(const int); + void createTrackletsBuffersArray(const int); void createCellsBuffers(const int); + void createCellsBuffersArray(const int); void createCellsDevice(); - void createCellsLUTDevice(); - void createNeighboursIndexTablesDevice(); - void createNeighboursDevice(const unsigned int layer, const unsigned int nNeighbours); - void createNeighboursDevice(const unsigned int layer, std::vector>& neighbours); + 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 createNeighboursDeviceArray(); - void createTrackITSExtDevice(bounded_vector&); - void downloadTrackITSExtDevice(bounded_vector&); + void createTrackITSExtDevice(const size_t); + void downloadTrackITSExtDevice(); void downloadCellsNeighboursDevice(std::vector>>&, const int); void downloadNeighboursLUTDevice(bounded_vector&, const int); void downloadCellsDevice(); void downloadCellsLUTDevice(); - void unregisterRest(); - template - Stream& getStream(const size_t stream) - { - return *mGpuStreams[stream]; - } - void wipe(const int); + + /// 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; } + IndexTableUtilsN* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } int* getDeviceROFramesClusters(const int layer) { return mROFramesClustersDevice[layer]; } auto& getTrackITSExt() { return mTrackITSExt; } Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } @@ -105,53 +125,73 @@ class TimeFrameGPU : public TimeFrame gpuPair* getDeviceNeighbourPairs(const int layer) { return mNeighbourPairsDevice[layer]; } std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } int* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } - int** getDeviceNeighboursArray() { return mNeighboursDeviceArray; } + int** getDeviceNeighboursArray() { return mNeighboursDevice.data(); } TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); const TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() const { return mTrackingFrameInfoDeviceArray; } const Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } const Cluster** getDeviceArrayUnsortedClusters() const { return mUnsortedClustersDeviceArray; } const int** getDeviceArrayClustersIndexTables() const { return mClustersIndexTablesDeviceArray; } std::vector getClusterSizes(); - const unsigned char** getDeviceArrayUsedClusters() const { return mUsedClustersDeviceArray; } - const int** getDeviceROframeClusters() const { return mROFrameClustersDeviceArray; } + uint8_t** getDeviceArrayUsedClusters() const { return mUsedClustersDeviceArray; } + const int** getDeviceROFrameClusters() const { return mROFramesClustersDeviceArray; } Tracklet** getDeviceArrayTracklets() { return mTrackletsDeviceArray; } int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } int** getDeviceArrayCellsLUT() const { return mCellsLUTDeviceArray; } int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } - CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } - CellSeed* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + CellSeedN** getDeviceArrayCells() { return mCellsDeviceArray; } + CellSeedN* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + int* getDeviceTrackSeedsLUT() { return mTrackSeedsLUTDevice; } + auto getNTrackSeeds() const { return mNTracks; } o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } uint8_t* getDeviceMultCutMask() { return mMultMaskDevice; } - void setDevicePropagator(const o2::base::PropagatorImpl*) override; + // 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 getNTracklets() { return mNTracklets; } gsl::span getNCells() { return mNCells; } - std::array& getArrayNCells() { 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 getDeviceTracklet() { return mTrackletsDevice; } - gsl::span getDeviceCells() { return mCellsDevice; } + gsl::span getDeviceTracklets() { return mTrackletsDevice; } + gsl::span getDeviceCells() { return mCellsDevice; } // Overridden getters - int getNumberOfCells() const; + 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; + 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; // Host-available device buffer sizes std::array mNTracklets; std::array mNCells; + std::array mNNeighbours; // Device pointers - IndexTableUtils* mIndexTableUtilsDevice; + IndexTableUtilsN* mIndexTableUtilsDevice; // Hybrid pref uint8_t* mMultMaskDevice; @@ -165,22 +205,24 @@ class TimeFrameGPU : public TimeFrame const Cluster** mClustersDeviceArray; const Cluster** mUnsortedClustersDeviceArray; const int** mClustersIndexTablesDeviceArray; - const unsigned char** mUsedClustersDeviceArray; - const int** mROFrameClustersDeviceArray; + uint8_t** mUsedClustersDeviceArray; + const int** mROFramesClustersDeviceArray; std::array mTrackletsDevice; - Tracklet** mTrackletsDeviceArray; std::array mTrackletsLUTDevice; std::array mCellsLUTDevice; std::array mNeighboursLUTDevice; - int** mCellsLUTDeviceArray; - int** mNeighboursCellDeviceArray; - int** mNeighboursCellLUTDeviceArray; - int** mTrackletsLUTDeviceArray; - std::array mCellsDevice; - std::array mNeighboursIndexTablesDevice; - CellSeed* mTrackSeedsDevice; - CellSeed** mCellsDeviceArray; + 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; @@ -190,14 +232,29 @@ class TimeFrameGPU : public TimeFrame TrackITSExt* mTrackITSExtDevice; std::array*, nLayers - 2> mNeighbourPairsDevice; std::array mNeighboursDevice; - int** mNeighboursDeviceArray; std::array mTrackingFrameInfoDevice; const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; + /// Vertexer + std::array mNTrackletsPerROFDevice; + std::array mNTrackletsPerClusterDevice; + std::array mNTrackletsPerClusterSumDevice; + uint8_t* mUsedTrackletsDevice; + int32_t* mNLinesPerClusterDevice; + int32_t* mNLinesPerClusterSumDevice; + int32_t** mNTrackletsPerROFDeviceArray; + int32_t** mNTrackletsPerClusterDeviceArray; + int32_t** mNTrackletsPerClusterSumDeviceArray; + Line* mLinesDevice; + // State - std::vector mGpuStreams; - size_t mAvailMemGB; - bool mFirstInit = true; + Streams mGpuStreams; + std::bitset mPinnedUnsortedClusters{0}; + std::bitset mPinnedClusters{0}; + std::bitset mPinnedClustersIndexTables{0}; + std::bitset mPinnedUsedClusters{0}; + std::bitset mPinnedROFramesClusters{0}; + std::bitset mPinnedTrackingFrameInfo{0}; // Temporary buffer for storing output tracks from GPU tracking bounded_vector mTrackITSExt; @@ -218,12 +275,24 @@ inline std::vector TimeFrameGPU::getClusterSizes() return sizes; } +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 1654f8cc8cf94..7d26e74692aa5 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h @@ -22,9 +22,11 @@ namespace o2::its template class TrackerTraitsGPU final : public TrackerTraits { + using typename TrackerTraits::IndexTableUtilsN; + public: TrackerTraitsGPU() = default; - ~TrackerTraitsGPU() override = default; + ~TrackerTraitsGPU() final = default; void adoptTimeFrame(TimeFrame* tf) final; void initialiseTimeFrame(const int iteration) final; @@ -48,8 +50,8 @@ class TrackerTraitsGPU final : public TrackerTraits int getTFNumberOfCells() const override; private: - IndexTableUtils* mDeviceIndexTableUtils; - gpu::TimeFrameGPU<7>* mTimeFrameGPU; + IndexTableUtilsN* mDeviceIndexTableUtils; + gpu::TimeFrameGPU* mTimeFrameGPU; }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index b847aacd9bba5..53992ccf3eb85 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -13,52 +13,30 @@ #ifndef ITSTRACKINGGPU_TRACKINGKERNELS_H_ #define ITSTRACKINGGPU_TRACKINGKERNELS_H_ +#include + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/Definitions.h" +#include "ITStrackingGPU/Utils.h" #include "DetectorsBase/Propagator.h" #include "GPUCommonDef.h" namespace o2::its { +template class CellSeed; +class TrackingFrameInfo; +class Tracklet; +template +class IndexTableUtils; +class Cluster; +class TrackITSExt; class ExternalAllocator; -namespace gpu -{ - -#ifdef GPUCA_GPUCODE // GPUg() global kernels must only when compiled by GPU compiler - -GPUdi() int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - -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); - -template -GPUg() void fitTrackSeedsKernel( - CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - o2::its::TrackITSExt* tracks, - const float* minPts, - const unsigned int 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 template -void countTrackletsInROFsHandler(const IndexTableUtils* utils, +void countTrackletsInROFsHandler(const IndexTableUtils* utils, const uint8_t* multMask, + const int layer, const int startROF, const int endROF, const int maxROF, @@ -83,12 +61,15 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, const int nBlocks, - const int nThreads); + const int nThreads, + gpu::Streams& streams); template -void computeTrackletsInROFsHandler(const IndexTableUtils* utils, +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const uint8_t* multMask, + const int layer, const int startROF, const int endROF, const int maxROF, @@ -116,9 +97,12 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, const int nBlocks, - const int nThreads); + const int nThreads, + gpu::Streams& streams); +template void countCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -126,16 +110,20 @@ void countCellsHandler(const Cluster** sortedClusters, int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + 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); + const int nThreads, + gpu::Streams& streams); +template void computeCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -143,35 +131,45 @@ void computeCellsHandler(const Cluster** sortedClusters, int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + 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); - -unsigned int countCellNeighboursHandler(CellSeed** cellsLayersDevice, - int* neighboursLUTs, - int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - 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); - -void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, + 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, @@ -179,45 +177,78 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, const int nBlocks, - const int nThreads); + 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, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, std::array& nCells, const unsigned char** usedClusters, std::array& neighbours, gsl::span neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, - bounded_vector& seedsHost, - o2::its::ExternalAllocator*, + 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); -void trackSeedHandler(CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - o2::its::TrackITSExt* tracks, - std::vector& minPtsHost, - const unsigned int nSeeds, - const float Bz, - const int startLevel, - float maxChi2ClusterAttachment, - float maxChi2NDF, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType, - const int nBlocks, - const int nThreads); +template +void countTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float Bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float Bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + } // namespace o2::its #endif // ITSTRACKINGGPU_TRACKINGKERNELS_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index 74c118009d67d..ee0a203f32fda 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -16,27 +16,46 @@ #ifndef ITSTRACKINGGPU_UTILS_H_ #define ITSTRACKINGGPU_UTILS_H_ +#include +#include +#include + +#include "ITStracking/MathUtils.h" +#include "ITStracking/ExternalAllocator.h" + #include "GPUCommonDef.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 -struct gpuPair { - T1 first; - T2 second; -}; +using gpuPair = std::pair; namespace gpu { -template -void discardResult(const T&) -{ -} - -// Poor man implementation of a span-like struct. It is very limited. template struct gpuSpan { using value_type = T; @@ -80,24 +99,379 @@ struct gpuSpan { unsigned int _size; }; -enum class Task { - Tracker = 0, - Vertexer = 1 +// Abstract stream class +class Stream +{ + 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}; +}; + +// Abstract vector for streams. +class Streams +{ + 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 + } + + private: + std::vector mStreams; +}; + +#ifdef ITS_MEASURE_GPU_TIME +class GPUTimer +{ + 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 + } + } + + 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 + +#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]); }; -template -GPUhd() T* getPtrFromRuler(int index, T* src, const int* ruler, const int stride = 1) +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) { - return src + ruler[index] * stride; + 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); } -template -GPUhd() const T* getPtrFromRuler(int index, const T* src, const int* ruler, const int stride = 1) +GPUdii() gpuSpan getNLinesPerCluster(const int rof, + const int totROFs, + const int** roframesClus, + const int* nlines) { - return src + ruler[index] * stride; + 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/VertexerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h index 5b1d9194e1174..dddc247466c65 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h @@ -31,32 +31,25 @@ namespace o2::its { -class VertexerTraitsGPU final : public VertexerTraits +template +class VertexerTraitsGPU final : public VertexerTraits { public: void initialise(const TrackingParameters&, const int iteration = 0) final; - void adoptTimeFrame(TimeFrame<7>*) noexcept final; + void adoptTimeFrame(TimeFrame* tf) noexcept final; void computeTracklets(const int iteration = 0) final; void computeTrackletMatching(const int iteration = 0) final; void computeVertices(const int iteration = 0) final; void updateVertexingParameters(const std::vector&, const TimeFrameGPUParameters&) final; - void computeVerticesHist(); bool isGPU() const noexcept final { return true; } const char* getName() const noexcept final { return "GPU"; } protected: - IndexTableUtils* mDeviceIndexTableUtils; - gpu::TimeFrameGPU<7>* mTimeFrameGPU; + gpu::TimeFrameGPU* mTimeFrameGPU; TimeFrameGPUParameters mTfGPUParams; }; -inline void VertexerTraitsGPU::adoptTimeFrame(TimeFrame<7>* tf) noexcept -{ - mTimeFrameGPU = static_cast*>(tf); - mTimeFrame = static_cast*>(tf); -} - } // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h index 059b1cdc29082..67f12bad8486c 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h @@ -12,46 +12,104 @@ #ifndef ITSTRACKINGGPU_VERTEXINGKERNELS_H_ #define ITSTRACKINGGPU_VERTEXINGKERNELS_H_ -#include "ITStracking/MathUtils.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/ClusterLines.h" -#include "ITStracking/Tracklet.h" +#include +#include +#include +#include "ITStracking/Tracklet.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/ClusterLines.h" #include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" -#include "ITStrackingGPU/TracerGPU.h" -namespace o2::its::gpu +namespace o2::its { -#ifdef GPUCA_GPUCODE // GPUg() global kernels must only when compiled by GPU compiler -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); -#endif -template -void trackletFinderHandler(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); -} // namespace o2::its::gpu + +/// 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 9769930504f29..e38dbb1ef20e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -11,29 +11,29 @@ # CUDA if(CUDA_ENABLED) -find_package(CUDAToolkit) -message(STATUS "Building ITS CUDA tracker") -# add_compile_options(-O0 -g -lineinfo -fPIC) -# add_compile_definitions(ITS_MEASURE_GPU_TIME) -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_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/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 8380533a28e04..da0cd51478945 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -9,249 +9,254 @@ // 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 +#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" -#ifdef ITS_MEASURE_GPU_TIME -#define START_GPU_STREAM_TIMER(stream, name) \ - cudaEvent_t event_start, event_stop; \ - GPUChkErrS(cudaEventCreate(&event_start)); \ - GPUChkErrS(cudaEventCreate(&event_stop)); \ - GPUChkErrS(cudaEventRecord(event_start, stream)); \ - const std::string task_name = name; - -#define STOP_GPU_STREAM_TIMER(stream) \ - GPUChkErrS(cudaEventRecord(event_stop, stream)); \ - GPUChkErrS(cudaEventSynchronize(event_stop)); \ - float ms; \ - GPUChkErrS(cudaEventElapsedTime(&ms, event_start, event_stop)); \ - std::cout << "Elapsed time for " << task_name << ": " << ms << " ms" << std::endl; \ - GPUChkErrS(cudaEventDestroy(event_start)); \ - GPUChkErrS(cudaEventDestroy(event_stop)); -#else -#define START_GPU_STREAM_TIMER(stream, name) -#define STOP_GPU_STREAM_TIMER(stream) -#endif - -namespace o2 +namespace o2::its::gpu { -namespace its -{ -using constants::GB; -using constants::MB; - -namespace gpu -{ -class Stream final -{ - public: - Stream(); - ~Stream(); - - [[nodiscard]] const cudaStream_t& get() const; - private: - cudaStream_t mStream; -}; - -Stream::Stream() +template +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) { - GPUChkErrS(cudaStreamCreate(&mStream)); + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, stream.get())); + } } -Stream::~Stream() +template +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) { - GPUChkErrS(cudaStreamDestroy(mStream)); + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMalloc(reinterpret_cast(ptr), size)); + } } -const cudaStream_t& Stream::get() const +template +void TimeFrameGPU::loadIndexTableUtils(const int iteration) { - return mStream; + 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)); } -void* DefaultGPUAllocator::allocate(size_t size) +template +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) { - LOGP(fatal, "Called DefaultGPUAllocator::allocate with size {}", size); - return nullptr; // to be implemented + 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); + } + } + } } template -TimeFrameGPU::TimeFrameGPU() +void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) { - this->mIsGPU = true; + 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 -TimeFrameGPU::~TimeFrameGPU() = default; +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); + } + } + } +} template -void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream* strPtr, bool extAllocator) +void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) { - if (extAllocator) { - *ptr = this->mAllocator->allocate(size); - } else { - LOGP(debug, "Calling default CUDA allocator"); - GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, strPtr->get())); + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); + GPULog("gpu-transfer: loading {} clusters on layer {}, for {:.2f} MB.", this->mClusters[layer].size(), layer, this->mClusters[layer].size() * sizeof(Cluster) / constants::MB); + allocMemAsync(reinterpret_cast(&mClustersDevice[layer]), this->mClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mClustersDevice[layer], this->mClusters[layer].data(), this->mClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mClustersDeviceArray[layer], &mClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } } template -void TimeFrameGPU::setDevicePropagator(const o2::base::PropagatorImpl* propagator) +void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) { - this->mPropagatorDevice = propagator; + 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); + } + } + } } template -void TimeFrameGPU::loadIndexTableUtils(const int iteration) +void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading indextable utils"); if (!iteration) { - LOGP(debug, "gpu-allocation: allocating IndexTableUtils buffer, for {} MB.", sizeof(IndexTableUtils) / MB); - allocMemAsync(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtils), nullptr, this->getExtAllocator()); + 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())); } - LOGP(debug, "gpu-transfer: loading IndexTableUtils object, for {} MB.", sizeof(IndexTableUtils) / MB); - GPUChkErrS(cudaMemcpyAsync(mIndexTableUtilsDevice, &(this->mIndexTableUtils), sizeof(IndexTableUtils), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template -void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration) +void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading unsorted clusters"); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} unsorted clusters on layer {}, for {} MB.", this->mUnsortedClusters[iLayer].size(), iLayer, this->mUnsortedClusters[iLayer].size() * sizeof(Cluster) / MB); - allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[iLayer]), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mUnsortedClustersDevice[iLayer], this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + GPUTimer timer("creating used clusters flags"); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(iLayer); + } } - allocMemAsync(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mUnsortedClustersDeviceArray, mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } } template -void TimeFrameGPU::loadClustersDevice(const int iteration) +void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) { if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading sorted clusters"); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} clusters on layer {}, for {} MB.", this->mClusters[iLayer].size(), iLayer, this->mClusters[iLayer].size() * sizeof(Cluster) / MB); - allocMemAsync(reinterpret_cast(&mClustersDevice[iLayer]), this->mClusters[iLayer].size() * sizeof(Cluster), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mClustersDevice[iLayer], this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - } - allocMemAsync(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mClustersDeviceArray, mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); + GPULog("gpu-transfer: creating {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[layer].size(), layer, this->mUsedClusters[layer].size() * sizeof(unsigned char) / constants::MB); + allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemsetAsync(mUsedClustersDevice[layer], 0, this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mUsedClustersDeviceArray[layer], &mUsedClustersDevice[layer], sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } } template -void TimeFrameGPU::loadClustersIndexTables(const int iteration) +void TimeFrameGPU::loadUsedClustersDevice() { - if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading sorted clusters"); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading clusters indextable for layer {} with {} elements, for {} MB.", iLayer, this->mIndexTables[iLayer].size(), this->mIndexTables[iLayer].size() * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[iLayer]), this->mIndexTables[iLayer].size() * sizeof(int), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mClustersIndexTablesDevice[iLayer], this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - } - allocMemAsync(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mClustersIndexTablesDeviceArray, mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUTimer timer(mGpuStreams[iLayer], "loading used clusters flags", iLayer); + GPULog("gpu-transfer: loading {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } template -void TimeFrameGPU::createUsedClustersDevice(const int iteration) +void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) { if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "creating used clusters flags"); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: creating {} used clusters flags on layer {}, for {} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / MB); - allocMemAsync(reinterpret_cast(&mUsedClustersDevice[iLayer]), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemsetAsync(mUsedClustersDevice[iLayer], 0, this->mUsedClusters[iLayer].size() * sizeof(unsigned char), mGpuStreams[0]->get())); + 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); + } } - allocMemAsync(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(unsigned char*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mUsedClustersDeviceArray, mUsedClustersDevice.data(), nLayers * sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } } template -void TimeFrameGPU::loadUsedClustersDevice() +void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading used clusters flags"); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} used clusters flags on layer {}, for {} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mClusters[iLayer].size() * sizeof(unsigned char) / MB); - GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + 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())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template -void TimeFrameGPU::loadROframeClustersDevice(const int iteration) +void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) { if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading ROframe clusters"); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} ROframe clusters info on layer {}, for {} MB.", this->mROFramesClusters[iLayer].size(), iLayer, this->mROFramesClusters[iLayer].size() * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[iLayer]), this->mROFramesClusters[iLayer].size() * sizeof(int), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mROFramesClustersDevice[iLayer], this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + 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); + } } - allocMemAsync(reinterpret_cast(&mROFrameClustersDeviceArray), nLayers * sizeof(int*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mROFrameClustersDeviceArray, mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } } template -void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration) +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading trackingframeinfo"); if (!iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} tfinfo on layer {}, for {} MB.", this->mTrackingFrameInfo[iLayer].size(), iLayer, this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo) / MB); - allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[iLayer]), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mTrackingFrameInfoDevice[iLayer], this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - } - allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mTrackingFrameInfoDeviceArray, mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + 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())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) { - if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading multiplicity cut mask"); - LOGP(debug, "gpu-transfer: loading multiplicity cut mask with {} elements, for {} MB.", this->mMultiplicityCutMask.size(), this->mMultiplicityCutMask.size() * sizeof(bool) / MB); - allocMemAsync(reinterpret_cast(&mMultMaskDevice), this->mMultiplicityCutMask.size() * sizeof(uint8_t), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mMultMaskDevice, this->mMultiplicityCutMask.data(), this->mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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)); } } @@ -259,315 +264,456 @@ template void TimeFrameGPU::loadVertices(const int iteration) { if (!iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading seeding vertices"); - LOGP(debug, "gpu-transfer: loading {} ROframes vertices, for {} MB.", this->mROFramesPV.size(), this->mROFramesPV.size() * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mROFramesPVDevice), this->mROFramesPV.size() * sizeof(int), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mROFramesPVDevice, this->mROFramesPV.data(), this->mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - LOGP(debug, "gpu-transfer: loading {} seeding vertices, for {} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / MB); - allocMemAsync(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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 -void TimeFrameGPU::createTrackletsLUTDevice(const int iteration) +void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "creating tracklets LUTs"); - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - if (!iteration) { - LOGP(debug, "gpu-transfer: creating tracklets LUT for {} elements on layer {}, for {} MB.", this->mClusters[iLayer].size() + 1, iLayer, (this->mClusters[iLayer].size() + 1) * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[iLayer]), (this->mClusters[iLayer].size() + 1) * sizeof(int), nullptr, this->getExtAllocator()); - } - GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[iLayer], 0, (this->mClusters[iLayer].size() + 1) * sizeof(int), mGpuStreams[0]->get())); + if (!iteration) { + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); } +} + +template +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "creating tracklets LUTs", layer); + const int ncls = this->mClusters[layer].size() + 1; if (!iteration) { - allocMemAsync(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), mTrackletsLUTDevice.size() * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + 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())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); } template -void TimeFrameGPU::createTrackletsBuffers() +void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "creating cells buffers"); - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - mNTracklets[iLayer] = 0; - GPUChkErrS(cudaMemcpyAsync(&mNTracklets[iLayer], mTrackletsLUTDevice[iLayer] + this->mClusters[iLayer].size(), sizeof(int), cudaMemcpyDeviceToHost)); - LOGP(debug, "gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {} MB.", mNTracklets[iLayer], iLayer, mNTracklets[iLayer] * sizeof(Tracklet) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iLayer]), mNTracklets[iLayer] * sizeof(Tracklet), nullptr, this->getExtAllocator()); + if (!iteration) { + GPUTimer timer("creating tracklet buffers array"); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); } - allocMemAsync(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mTrackletsDeviceArray, mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); +} + +template +void TimeFrameGPU::createTrackletsBuffers(const int 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 void TimeFrameGPU::loadTrackletsDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading tracklets"); + GPUTimer timer(mGpuStreams, "loading tracklets", nLayers - 1); for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} tracklets on layer {}, for {} MB.", this->mTracklets[iLayer].size(), iLayer, this->mTracklets[iLayer].size() * sizeof(Tracklet) / MB); + 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[0]->get())); + GPUChkErrS(cudaMemcpyAsync(mTrackletsDevice[iLayer], this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template void TimeFrameGPU::loadTrackletsLUTDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading tracklets"); + GPUTimer timer("loading tracklets"); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {} MB", this->mTrackletsLookupTable[iLayer].size(), iLayer + 1, this->mTrackletsLookupTable[iLayer].size() * sizeof(int) / MB); - GPUChkErrS(cudaHostRegister(this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); + 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())); } - GPUChkErrS(cudaHostRegister(mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + mGpuStreams.sync(); + GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); } template -void TimeFrameGPU::createNeighboursIndexTablesDevice() +void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "creating cells neighbours"); - // Here we do also the creation of the CellsDeviceArray, as the cells buffers are populated separately in the previous steps. - allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: loading neighbours LUT for {} elements on layer {}, for {} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeed) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (mNCells[iLayer] + 1) * sizeof(int), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[iLayer], 0, (mNCells[iLayer] + 1) * sizeof(int), mGpuStreams[0]->get())); - } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + GPUTimer timer(mGpuStreams[layer], "creating cells neighbours", layer); + GPULog("gpu-transfer: reserving neighbours LUT for {} elements on layer {}, for {:.2f} MB.", mNCells[layer] + 1, layer, (mNCells[layer] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[layer]), (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[layer], 0, (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); } template void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "reserving neighboursLUT"); - LOGP(debug, "gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), nullptr, this->getExtAllocator()); // We need one element more to move exc -> inc - GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + GPUTimer timer(mGpuStreams[layer], "reserving neighboursLUT"); + GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); // We need one element more to move exc -> inc + GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); } template void TimeFrameGPU::loadCellsDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading cell seeds"); + GPUTimer timer(mGpuStreams, "loading cell seeds", nLayers - 2); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} cell seeds on layer {}, for {} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeed) / MB); - allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeed), nullptr, this->getExtAllocator()); - allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), nullptr, this->getExtAllocator()); // accessory for the neigh. finding. - GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[iLayer], 0, (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[0]->get())); - GPUChkErrS(cudaMemcpyAsync(mCellsDevice[iLayer], this->mCells[iLayer].data(), this->mCells[iLayer].size() * sizeof(CellSeed), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + 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())); } - allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template -void TimeFrameGPU::createCellsLUTDevice() +void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "creating cells LUTs"); - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: creating cell LUT for {} elements on layer {}, for {} MB.", mNTracklets[iLayer] + 1, iLayer, (mNTracklets[iLayer] + 1) * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mCellsLUTDevice[iLayer]), (mNTracklets[iLayer] + 1) * sizeof(int), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[iLayer], 0, (mNTracklets[iLayer] + 1) * sizeof(int), mGpuStreams[0]->get())); + if (!iteration) { + GPUTimer timer("creating cells LUTs array"); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); + } +} + +template +void TimeFrameGPU::createCellsLUTDevice(const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); + GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer] + 1, layer, (mNTracklets[layer] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[layer], 0, (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::createCellsBuffersArray(const int iteration) +{ + if (!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)); } - allocMemAsync(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), nullptr, this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mCellsLUTDeviceArray, mCellsLUTDevice.data(), mCellsLUTDevice.size() * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template void TimeFrameGPU::createCellsBuffers(const int layer) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "creating cells buffers"); + GPUTimer timer(mGpuStreams[layer], "creating cells buffers"); mNCells[layer] = 0; - GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost)); - LOGP(debug, "gpu-transfer: creating cell buffer for {} elements on layer {}, for {} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeed) / MB); - allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeed), nullptr, this->getExtAllocator()); - - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of cells is correct + GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeedN) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemcpyAsync(&mCellsDeviceArray[layer], &mCellsDevice[layer], sizeof(CellSeedN*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } template void TimeFrameGPU::loadCellsLUTDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading cells LUTs"); + GPUTimer timer(mGpuStreams, "loading cells LUTs", nLayers - 3); for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - LOGP(debug, "gpu-transfer: loading cell LUT for {} elements on layer {}, for {} MB.", this->mCellsLookupTable[iLayer].size(), iLayer, this->mCellsLookupTable[iLayer].size() * sizeof(int) / MB); + 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[0]->get())); + GPUChkErrS(cudaMemcpyAsync(mCellsLUTDevice[iLayer + 1], this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template void TimeFrameGPU::loadRoadsDevice() { - LOGP(debug, "gpu-transfer: loading {} roads, for {} MB.", this->mRoads.size(), this->mRoads.size() * sizeof(Road) / MB); - allocMemAsync(reinterpret_cast(&mRoadsDevice), this->mRoads.size() * sizeof(Road), mGpuStreams[0], this->getExtAllocator()); + 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(cudaMemcpyAsync(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); + GPUChkErrS(cudaMemcpy(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice)); +} + +template +void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) +{ + GPUTimer timer("loading track seeds"); + GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(CellSeedN) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(CellSeedN), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemcpy(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(CellSeedN), cudaMemcpyHostToDevice)); + GPULog("gpu-transfer: creating {} track seeds LUT, for {:.2f} MB.", seeds.size() + 1, (seeds.size() + 1) * sizeof(int) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsLUTDevice), (seeds.size() + 1) * sizeof(int), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackSeedsLUTDevice, 0, (seeds.size() + 1) * sizeof(int))); +} + +template +void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) +{ + 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::loadTrackSeedsDevice(bounded_vector& seeds) +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "loading track seeds"); - 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], this->getExtAllocator()); - GPUChkErrS(cudaHostRegister(seeds.data(), seeds.size() * sizeof(CellSeed), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpyAsync(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(CellSeed), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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::createNeighboursDevice(const unsigned int layer, const unsigned int nNeighbours) +void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "reserving neighbours"); - LOGP(debug, "gpu-allocation: reserving {} neighbours (pairs), for {} MB.", nNeighbours, nNeighbours * sizeof(gpuPair) / MB); - allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), nNeighbours * sizeof(gpuPair), mGpuStreams[0], this->getExtAllocator()); - GPUChkErrS(cudaMemsetAsync(mNeighbourPairsDevice[layer], -1, nNeighbours * sizeof(gpuPair), mGpuStreams[0]->get())); - LOGP(debug, "gpu-allocation: reserving {} neighbours, for {} MB.", nNeighbours, nNeighbours * sizeof(gpuPair) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), nNeighbours * sizeof(int), mGpuStreams[0], this->getExtAllocator()); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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()); + + 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()); + + 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()); + } + GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[iMode], 0, ncls * sizeof(int32_t), mGpuStreams[iMode].get())); + GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterSumDevice[iMode], 0, (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); + GPUChkErrS(cudaMemsetAsync(mNTrackletsPerROFDevice[iMode], 0, (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); + } + mGpuStreams[0].sync(); + mGpuStreams[1].sync(); + if (!iteration) { + allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterDeviceArray, mNTrackletsPerClusterDevice.data(), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); + + allocMem(reinterpret_cast(&mNTrackletsPerClusterSumDeviceArray), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterSumDeviceArray, mNTrackletsPerClusterSumDevice.data(), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); + + allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); + } } template -void TimeFrameGPU::createNeighboursDevice(const unsigned int layer, std::vector>& neighbours) +void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "reserving neighbours"); - this->mCellsNeighbours[layer].clear(); - this->mCellsNeighbours[layer].resize(neighbours.size()); - LOGP(debug, "gpu-allocation: reserving {} neighbours (pairs), for {} MB.", neighbours.size(), neighbours.size() * sizeof(gpuPair) / MB); - allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), neighbours.size() * sizeof(gpuPair), mGpuStreams[0], this->getExtAllocator()); - GPUChkErrS(cudaMemsetAsync(mNeighbourPairsDevice[layer], -1, neighbours.size() * sizeof(gpuPair), mGpuStreams[0]->get())); - LOGP(debug, "gpu-allocation: reserving {} neighbours, for {} MB.", neighbours.size(), neighbours.size() * sizeof(gpuPair) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), neighbours.size() * sizeof(int), mGpuStreams[0], this->getExtAllocator()); - STOP_GPU_STREAM_TIMER(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()); + } + 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::createNeighboursDeviceArray() +void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "reserving neighbours"); - allocMemAsync(reinterpret_cast(&mNeighboursDeviceArray), (nLayers - 2) * sizeof(int*), mGpuStreams[0], this->getExtAllocator()); - GPUChkErrS(cudaMemcpyAsync(mNeighboursDeviceArray, mNeighboursDevice.data(), (nLayers - 2) * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(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::createTrackITSExtDevice(bounded_vector& seeds) +void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "reserving tracks"); - mTrackITSExt = bounded_vector(seeds.size(), {}, this->getMemoryPool().get()); - 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], this->getExtAllocator()); - GPUChkErrS(cudaMemsetAsync(mTrackITSExtDevice, 0, seeds.size() * sizeof(o2::its::TrackITSExt), mGpuStreams[0]->get())); - GPUChkErrS(cudaHostRegister(mTrackITSExt.data(), seeds.size() * sizeof(o2::its::TrackITSExt), cudaHostRegisterPortable)); - STOP_GPU_STREAM_TIMER(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() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "downloading cells"); + GPUTimer timer(mGpuStreams, "downloading cells", nLayers - 2); for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: downloading {} cells on layer: {}, for {} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeed) / MB); + 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(CellSeed), cudaMemcpyDeviceToHost, mGpuStreams[0]->get())); + GPUChkErrS(cudaMemcpyAsync(this->mCells[iLayer].data(), this->mCellsDevice[iLayer], mNCells[iLayer] * sizeof(CellSeedN), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template void TimeFrameGPU::downloadCellsLUTDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "downloading cell luts"); + GPUTimer timer(mGpuStreams, "downloading cell luts", nLayers - 3); for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - LOGP(debug, "gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); + 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[0]->get())); + GPUChkErrS(cudaMemcpyAsync(this->mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); } template void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), fmt::format("downloading neighbours from layer {}", layer)); - LOGP(debug, "gpu-transfer: downloading {} neighbours, for {} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(std::pair) / MB); - // TODO: something less dangerous than assuming the same memory layout of std::pair and gpuPair... or not? :) - GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighbourPairsDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, 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) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), fmt::format("downloading neighbours LUT from layer {}", layer)); - LOGP(debug, "gpu-transfer: downloading neighbours LUT for {} elements on layer {}, for {} MB.", lut.size(), layer, lut.size() * sizeof(int) / MB); - GPUChkErrS(cudaMemcpyAsync(lut.data(), mNeighboursLUTDevice[layer], lut.size() * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[0]->get())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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(bounded_vector& seeds) +void TimeFrameGPU::downloadTrackITSExtDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "downloading tracks"); - LOGP(debug, "gpu-transfer: downloading {} tracks, for {} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / MB); - GPUChkErrS(cudaMemcpyAsync(mTrackITSExt.data(), mTrackITSExtDevice, seeds.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost, mGpuStreams[0]->get())); - GPUChkErrS(cudaHostUnregister(mTrackITSExt.data())); - GPUChkErrS(cudaHostUnregister(seeds.data())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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::unregisterRest() +void TimeFrameGPU::unregisterHostMemory(const int maxLayers) { - START_GPU_STREAM_TIMER(mGpuStreams[0]->get(), "unregistering rest of the host memory"); - LOGP(debug, "unregistering rest of the host memory..."); - GPUChkErrS(cudaHostUnregister(mCellsDevice.data())); - GPUChkErrS(cudaHostUnregister(mTrackletsDevice.data())); - STOP_GPU_STREAM_TIMER(mGpuStreams[0]->get()); + 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); + } + 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::unregisterHostMemory(const int maxLayers) +void TimeFrameGPU::pushMemoryStack(const int iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - GPUChkErrS(cudaHostUnregister(this->mUnsortedClusters[iLayer].data())); - GPUChkErrS(cudaHostUnregister(this->mClusters[iLayer].data())); - GPUChkErrS(cudaHostUnregister(this->mTrackingFrameInfo[iLayer].data())); - } - GPUChkErrS(cudaHostUnregister(mTrackingFrameInfoDevice.data())); - GPUChkErrS(cudaHostUnregister(mUnsortedClustersDevice.data())); - GPUChkErrS(cudaHostUnregister(mClustersDevice.data())); + // mark the beginning of memory marked with MEMORY_STACK that can be discarded + // after doing one iteration + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); +} + +template +void TimeFrameGPU::popMemoryStack(const int iteration) +{ + // pop all memory on the stack from this iteration + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); } template void TimeFrameGPU::initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers, - IndexTableUtils* utils, + IndexTableUtilsN* utils, const TimeFrameGPUParameters* gpuParam) { - mGpuStreams.resize(mGpuParams.nTimeFrameChunks); - for (auto& str : mGpuStreams) { - str = new Stream(); - } + mGpuStreams.resize(nLayers); o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); } +template +void TimeFrameGPU::syncStream(const size_t stream) +{ + mGpuStreams[stream].sync(); +} + +template +void TimeFrameGPU::syncStreams(const bool device) +{ + mGpuStreams.sync(device); +} + +template +void TimeFrameGPU::waitEvent(const int stream, const int event) +{ + mGpuStreams.waitEvent(stream, event); +} + +template +void TimeFrameGPU::recordEvent(const int event) +{ + mGpuStreams[event].record(); +} + +template +void TimeFrameGPU::recordEvents(const int start, const int end) +{ + for (int i{start}; i < end; ++i) { + recordEvent(i); + } +} + +template +void TimeFrameGPU::wipe() +{ + unregisterHostMemory(0); + o2::its::TimeFrame::wipe(); +} + template class TimeFrameGPU<7>; -} // namespace gpu -} // namespace its -} // namespace o2 +} // namespace o2::its::gpu diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 89d2b5aeffe63..42d2227de60f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -11,6 +11,7 @@ /// #include +#include #include #include "DataFormatsITS/TrackITS.h" @@ -18,24 +19,33 @@ #include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" #include "ITStracking/TrackingConfigParam.h" +#include "ITStracking/Constants.h" namespace o2::its { -constexpr int UnusedIndex{-1}; template void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) { mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], nLayers); - mTimeFrameGPU->loadClustersDevice(iteration); - mTimeFrameGPU->loadUnsortedClustersDevice(iteration); - mTimeFrameGPU->loadClustersIndexTables(iteration); - mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration); - mTimeFrameGPU->loadMultiplicityCutMask(iteration); + // on default stream mTimeFrameGPU->loadVertices(iteration); - mTimeFrameGPU->loadROframeClustersDevice(iteration); - mTimeFrameGPU->createUsedClustersDevice(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 @@ -48,45 +58,33 @@ void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) template void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) { - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - mTimeFrameGPU->createTrackletsLUTDevice(iteration); + const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - const Vertex diamondVert({this->mTrkParams[iteration].Diamond[0], this->mTrkParams[iteration].Diamond[1], this->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); - int startROF{this->mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * this->mTrkParams[iteration].nROFsPerIterations : 0}; - int endROF{o2::gpu::CAMath::Min(this->mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * this->mTrkParams[iteration].nROFsPerIterations + this->mTrkParams[iteration].DeltaROF : mTimeFrameGPU->getNrof(), mTimeFrameGPU->getNrof())}; + 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); + } - countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), - startROF, - endROF, - mTimeFrameGPU->getNrof(), - this->mTrkParams[iteration].DeltaROF, - iVertex, - mTimeFrameGPU->getDeviceVertices(), - mTimeFrameGPU->getDeviceROFramesPV(), - mTimeFrameGPU->getPrimaryVerticesNum(), - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getClusterSizes(), - mTimeFrameGPU->getDeviceROframeClusters(), - mTimeFrameGPU->getDeviceArrayUsedClusters(), - mTimeFrameGPU->getDeviceArrayClustersIndexTables(), - mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getDeviceTrackletsLUTs(), // Required for the exclusive sums - iteration, - this->mTrkParams[iteration].NSigmaCut, - mTimeFrameGPU->getPhiCuts(), - this->mTrkParams[iteration].PVres, - mTimeFrameGPU->getMinRs(), - mTimeFrameGPU->getMaxRs(), - mTimeFrameGPU->getPositionResolutions(), - this->mTrkParams[iteration].LayerRadii, - mTimeFrameGPU->getMSangles(), - conf.nBlocks, - conf.nThreads); - mTimeFrameGPU->createTrackletsBuffers(); - computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + 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(), @@ -97,12 +95,9 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getPrimaryVerticesNum(), mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getClusterSizes(), - mTimeFrameGPU->getDeviceROframeClusters(), - mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), mTimeFrameGPU->getDeviceArrayClustersIndexTables(), - mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceTracklet(), - mTimeFrameGPU->getNTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), mTimeFrameGPU->getDeviceTrackletsLUTs(), iteration, @@ -114,108 +109,183 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), - conf.nBlocks, - conf.nThreads); + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksLayerTracklets[iteration], + conf.nThreadsLayerTracklets[iteration], + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createTrackletsBuffers(iLayer); + if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { + continue; + } + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + iLayer, + startROF, + endROF, + mTimeFrameGPU->getNrof(), + this->mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceTracklets(), + mTimeFrameGPU->getNTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + iteration, + this->mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + this->mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + this->mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksLayerTracklets[iteration], + conf.nThreadsLayerTracklets[iteration], + mTimeFrameGPU->getStreams()); + } } template void TrackerTraitsGPU::computeLayerCells(const int iteration) { - mTimeFrameGPU->createCellsLUTDevice(); auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - for (int iLayer = 0; iLayer < this->mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - if (!mTimeFrameGPU->getNTracklets()[iLayer + 1] || !mTimeFrameGPU->getNTracklets()[iLayer]) { - continue; + // start by queuing loading needed of three last layers + for (int iLayer{nLayers}; iLayer-- > nLayers - 3;) { + mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer); + mTimeFrameGPU->recordEvent(iLayer); + } + + for (int iLayer{this->mTrkParams[iteration].CellsPerRoad()}; iLayer--;) { + if (iLayer) { + mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer - 1); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer - 1); + mTimeFrameGPU->recordEvent(iLayer - 1); } + + // if there are no tracklets skip entirely const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[iLayer])}; - countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getDeviceArrayUnsortedClusters(), - mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), - mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getNTracklets()[iLayer], - iLayer, - nullptr, - mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceCellLUTs()[iLayer], - this->mBz, - this->mTrkParams[iteration].MaxChi2ClusterAttachment, - this->mTrkParams[iteration].CellDeltaTanLambdaSigma, - this->mTrkParams[iteration].NSigmaCut, - conf.nBlocks, - conf.nThreads); + if (!currentLayerTrackletsNum || !mTimeFrameGPU->getNTracklets()[iLayer + 1]) { + mTimeFrameGPU->getNCells()[iLayer] = 0; + continue; + } + + mTimeFrameGPU->createCellsLUTDevice(iLayer); + mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available + mTimeFrameGPU->waitEvent(iLayer, iLayer + 2); // wait stream until all data is available + countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + currentLayerTrackletsNum, + iLayer, + nullptr, + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceCellLUTs()[iLayer], + this->mTrkParams[iteration].DeltaROF, + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].CellDeltaTanLambdaSigma, + this->mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksLayerCells[iteration], + conf.nThreadsLayerCells[iteration], + mTimeFrameGPU->getStreams()); mTimeFrameGPU->createCellsBuffers(iLayer); - computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getDeviceArrayUnsortedClusters(), - mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), - mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getNTracklets()[iLayer], - iLayer, - mTimeFrameGPU->getDeviceCells()[iLayer], - mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceCellLUTs()[iLayer], - this->mBz, - this->mTrkParams[iteration].MaxChi2ClusterAttachment, - this->mTrkParams[iteration].CellDeltaTanLambdaSigma, - this->mTrkParams[iteration].NSigmaCut, - conf.nBlocks, - conf.nThreads); + if (mTimeFrameGPU->getNCells()[iLayer] == 0) { + continue; + } + computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + currentLayerTrackletsNum, + iLayer, + mTimeFrameGPU->getDeviceCells()[iLayer], + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceCellLUTs()[iLayer], + this->mTrkParams[iteration].DeltaROF, + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].CellDeltaTanLambdaSigma, + this->mTrkParams[iteration].NSigmaCut, + conf.nBlocksLayerCells[iteration], + conf.nThreadsLayerCells[iteration], + mTimeFrameGPU->getStreams()); } } template void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { - mTimeFrameGPU->createNeighboursIndexTablesDevice(); - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - for (int iLayer{0}; iLayer < this->mTrkParams[iteration].CellsPerRoad() - 1; ++iLayer) { - const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; + const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - if (!nextLayerCellsNum) { + 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); - unsigned int nNeigh = countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), - mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), // LUT is initialised here. - mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), - mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), - this->mTrkParams[0].MaxChi2ClusterAttachment, - this->mBz, - iLayer, - mTimeFrameGPU->getNCells()[iLayer], - nextLayerCellsNum, - 1e2, - conf.nBlocks, - conf.nThreads); - - mTimeFrameGPU->createNeighboursDevice(iLayer, nNeigh); - - computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), - mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), - mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), - mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), - this->mTrkParams[0].MaxChi2ClusterAttachment, - this->mBz, - iLayer, - mTimeFrameGPU->getNCells()[iLayer], - nextLayerCellsNum, - 1e2, - conf.nBlocks, - conf.nThreads); - - filterCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), - mTimeFrameGPU->getDeviceNeighbours(iLayer), - nNeigh, - mTimeFrameGPU->getExternalAllocator()); + 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->createNeighboursDeviceArray(); - mTimeFrameGPU->unregisterRest(); -}; + mTimeFrameGPU->syncStreams(false); +} template void TrackerTraitsGPU::findRoads(const int iteration) @@ -223,7 +293,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; - bounded_vector trackSeeds(this->getMemoryPool().get()); + 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; @@ -233,43 +303,71 @@ void TrackerTraitsGPU::findRoads(const int iteration) mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceCells()[startLayer], mTimeFrameGPU->getArrayNCells(), - mTimeFrameGPU->getDeviceArrayUsedClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), mTimeFrameGPU->getDeviceNeighboursAll(), mTimeFrameGPU->getDeviceNeighboursLUTs(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), trackSeeds, - mTimeFrameGPU->getExternalAllocator(), this->mBz, this->mTrkParams[0].MaxChi2ClusterAttachment, this->mTrkParams[0].MaxChi2NDF, mTimeFrameGPU->getDevicePropagator(), - this->mCorrType, - conf.nBlocks, - conf.nThreads); + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksProcessNeighbours[iteration], + conf.nThreadsProcessNeighbours[iteration]); } // fixme: I don't want to move tracks back and forth, but I need a way to use a thrust::allocator that is aware of our managed memory. - if (!trackSeeds.size()) { - LOGP(info, "No track seeds found, skipping track finding"); + 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 - this->mTrkParams[iteration].MinPt, // std::vector& minPtsHost, - trackSeeds.size(), // const size_t nSeeds - this->mBz, // const float Bz - startLevel, // const int startLevel, - this->mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment - this->mTrkParams[0].MaxChi2NDF, // float maxChi2NDF - mTimeFrameGPU->getDevicePropagator(), // const o2::base::Propagator* propagator - this->mCorrType, // o2::base::PropagatorImpl::MatCorrType - conf.nBlocks, - conf.nThreads); - - 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(); @@ -280,7 +378,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) int nShared = 0; bool isFirstShared{false}; for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == UnusedIndex) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } nShared += int(mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); @@ -293,7 +391,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) std::array rofs{INT_MAX, INT_MAX, INT_MAX}; for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == UnusedIndex) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } mTimeFrameGPU->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); @@ -317,9 +415,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) } mTimeFrameGPU->loadUsedClustersDevice(); } - if (iteration == this->mTrkParams.size() - 1) { - mTimeFrameGPU->unregisterHostMemory(0); - } + // wipe the artefact memory + mTimeFrameGPU->popMemoryStack(iteration); }; template diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 2191880374548..eacf514c7a91d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -23,27 +22,19 @@ #include #include #include -#include #include "ITStracking/Constants.h" +#include "ITStracking/Definitions.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/MathUtils.h" #include "ITStracking/ExternalAllocator.h" +#include "ITStracking/Tracklet.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/Cell.h" #include "DataFormatsITS/TrackITS.h" -#include "ReconstructionDataFormats/Vertex.h" - -#include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" #include "ITStrackingGPU/Utils.h" - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -#define GPU_BLOCKS GPUCA_DETERMINISTIC_CODE(1, 99999) -#define GPU_THREADS GPUCA_DETERMINISTIC_CODE(1, 99999) +#include "utils/strtag.h" // O2 track model #include "ReconstructionDataFormats/Track.h" @@ -52,105 +43,70 @@ using namespace o2::track; namespace o2::its { -using Vertex = o2::dataformats::Vertex>; - namespace gpu { -template -class TypedAllocator : public thrust::device_allocator -{ - public: - using value_type = T; - using pointer = T*; - - template - struct rebind { - using other = TypedAllocator; - }; - - explicit TypedAllocator(ExternalAllocator* allocPtr) - : mInternalAllocator(allocPtr) {} - - T* allocate(size_t n) - { - return reinterpret_cast(mInternalAllocator->allocate(n * sizeof(T))); - } - - void deallocate(T* p, size_t n) - { - char* raw_ptr = reinterpret_cast(p); - size_t bytes = n * sizeof(T); - mInternalAllocator->deallocate(raw_ptr, bytes); // redundant as internal dealloc is no-op. - } - - private: - ExternalAllocator* mInternalAllocator; -}; - -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::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 getEmptyBinsRect(); - } - - 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))}; -} - -GPUd() 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) +GPUdii() bool fitTrack(TrackITSExt& track, + int start, + int end, + int step, + float chi2clcut, + float chi2ndfcut, + float maxQoverPt, + int nCl, + float bz, + const TrackingFrameInfo** tfInfos, + const o2::base::Propagator* prop, + o2::base::PropagatorF::MatCorrType matCorrType, + o2::track::TrackPar* linRef, + const bool shiftRefToCluster) { for (int iLayer{start}; iLayer != end; iLayer += step) { if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } const TrackingFrameInfo& trackingHit = tfInfos[iLayer][track.getClusterIndex(iLayer)]; - if (!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) { - 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)) { return false; } + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + const float xx0 = (iLayer > 2) ? 1.e-2f : 5.e-3f; // Rough layer thickness + if (!track.correctForMaterial(*linRef, xx0, xx0 * constants::Radl * constants::Rho, true)) { + return false; + } + } + } else { + if (!track.o2::track::TrackParCovF::rotate(trackingHit.alphaTrackingFrame)) { + return false; + } + if (!prop->propagateToX(track, + trackingHit.xTrackingFrame, + bz, + o2::base::PropagatorImpl::MAX_SIN_PHI, + o2::base::PropagatorImpl::MAX_STEP, + matCorrType)) { + return false; + } + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + const float xx0 = (iLayer > 2) ? 1.e-2f : 5.e-3f; // Rough layer thickness + if (!track.correctForMaterial(xx0, xx0 * constants::Radl * constants::Rho, true)) { + return false; + } + } } auto predChi2{track.getPredictedChi2(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; - if ((nCl >= 3 && predChi2 > chi2clcut) || predChi2 < 0.f) { return false; } @@ -158,47 +114,107 @@ 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::CAMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); } -GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, - const Cluster& cluster2, - const TrackingFrameInfo& tf3, - const float bz) +GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, + const Cluster& cluster2, + const TrackingFrameInfo& tf3, + const float bz, + const bool reverse = false) { - const float ca = o2::gpu::CAMath::Cos(tf3.alphaTrackingFrame), sa = o2::gpu::CAMath::Sin(tf3.alphaTrackingFrame); + 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 z1 = cluster1.zCoordinate; const float x2 = cluster2.xCoordinate * ca + cluster2.yCoordinate * sa; const float y2 = -cluster2.xCoordinate * sa + cluster2.yCoordinate * ca; - const float z2 = cluster2.zCoordinate; const float x3 = tf3.xTrackingFrame; const float y3 = tf3.positionTrackingFrame[0]; - const float z3 = tf3.positionTrackingFrame[1]; - - const bool zeroField{o2::gpu::CAMath::Abs(bz) < o2::constants::math::Almost0}; - const float tgp = zeroField ? o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1) : 1.f; - const float crv = zeroField ? 1.f : math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); - const float snp = zeroField ? tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp) : crv * (x3 - math_utils::computeCurvatureCentreX(x3, y3, x2, y2, x1, y1)); - const float tgl12 = math_utils::computeTanDipAngle(x1, y1, x2, y2, z1, z2); - const float tgl23 = math_utils::computeTanDipAngle(x2, y2, x3, y3, z2, z3); - const float q2pt = zeroField ? 1.f / o2::track::kMostProbablePt : crv / (bz * o2::constants::math::B2C); - const float q2pt2 = crv * crv; - const float sg2q2pt = o2::track::kC1Pt2max * (q2pt2 > 0.0005 ? (q2pt2 < 1 ? q2pt2 : 1) : 0.0005); - return track::TrackParCov(tf3.xTrackingFrame, tf3.alphaTrackingFrame, - {y3, z3, snp, 0.5f * (tgl12 + tgl23), 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}); + + float snp, q2pt, q2pt2; + if (o2::gpu::CAMath::Abs(bz) < 0.01f) { + const float tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); + snp = sign * tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + q2pt = sign / track::kMostProbablePt; + q2pt2 = 1.f; + } else { + const float crv = math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); + snp = sign * crv * (x3 - math_utils::computeCurvatureCentreX(x3, y3, x2, y2, x1, y1)); + q2pt = sign * crv / (bz * o2::constants::math::B2C); + q2pt2 = crv * crv; + } + + const float tgl = 0.5f * (math_utils::computeTanDipAngle(x1, y1, x2, y2, cluster1.zCoordinate, cluster2.zCoordinate) + + math_utils::computeTanDipAngle(x2, y2, x3, y3, cluster2.zCoordinate, tf3.positionTrackingFrame[1])); + const float sg2q2pt = track::kC1Pt2max * (q2pt2 > 0.0005f ? (q2pt2 < 1.f ? q2pt2 : 1.f) : 0.0005f); + + return {x3, tf3.alphaTrackingFrame, {y3, tf3.positionTrackingFrame[1], snp, tgl, q2pt}, {tf3.covarianceTrackingFrame[0], tf3.covarianceTrackingFrame[1], tf3.covarianceTrackingFrame[2], 0.f, 0.f, track::kCSnp2max, 0.f, 0.f, 0.f, track::kCTgl2max, 0.f, 0.f, 0.f, 0.f, sg2q2pt}}; +} + +template +GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const float* layerRadii, + const float bz, + const int reseedIfShorter) +{ + TrackITSExt temporaryTrack(seed); + int lrMin = nLayers, lrMax = 0, lrMid = 0; + for (int iL{0}; iL < nLayers; ++iL) { + const int idx = seed.getCluster(iL); + temporaryTrack.setExternalClusterIndex(iL, idx, idx != constants::UnusedIndex); + if (idx != constants::UnusedIndex) { + // TODO only works if does not have holes + lrMin = o2::gpu::CAMath::Min(lrMin, iL); + lrMax = o2::gpu::CAMath::Max(lrMax, iL); + } + } + const int ncl = temporaryTrack.getNClusters(); + if (ncl < reseedIfShorter && ncl > 0) { // need to check if there are any clusters since we keep invalidate seeeds around + if (ncl == nLayers) { + lrMin = 0; + lrMax = nLayers - 1; + lrMid = (lrMin + lrMax) / 2; + } else { + lrMid = lrMin + 1; + float midR = 0.5f * (layerRadii[lrMax] + layerRadii[lrMin]), dstMidR = o2::gpu::CAMath::Abs(midR - layerRadii[lrMid]); + for (int iL = lrMid + 1; iL < lrMax; ++iL) { // find the midpoint as closest to the midR + auto dst = o2::gpu::GPUCommonMath::Abs(midR - layerRadii[iL]); + if (dst < dstMidR) { + lrMid = iL; + dstMidR = dst; + } + } + } + const auto& cluster0_tf = foundTrackingFrameInfo[lrMin][seed.getCluster(lrMin)]; + const auto& cluster1_gl = unsortedClusters[lrMid][seed.getCluster(lrMid)]; + const auto& cluster2_gl = unsortedClusters[lrMax][seed.getCluster(lrMax)]; + temporaryTrack.getParamIn() = buildTrackSeed(cluster2_gl, cluster1_gl, cluster0_tf, bz, true); + } + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + return temporaryTrack; } struct sort_tracklets { - GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex < b.firstClusterIndex || (a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex < b.secondClusterIndex); } + 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 { @@ -242,12 +258,13 @@ struct is_valid_pair { } }; +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 + GPUhd() bool operator()(const CellSeed& seed) const { return !(seed.getQ2Pt() > maxQ2Pt || seed.getChi2() > maxChi2); } @@ -260,58 +277,36 @@ struct compare_track_chi2 { } }; -GPUd() 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); -}; - -GPUd() 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); -} - -template -GPUg() void fitTrackSeedsKernel( - CellSeed* trackSeeds, +template +GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( + CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, + maybe_const* seedLUT, + const float* layerRadii, const float* minPts, const unsigned int nSeeds, const float bz, const int startLevel, - 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::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, @@ -323,14 +318,17 @@ GPUg() void fitTrackSeedsKernel( 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, @@ -342,21 +340,58 @@ GPUg() void fitTrackSeedsKernel( 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 || 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** cellSeedArray, +template +GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( + CellSeed** cellSeedArray, int* neighboursLUT, int* neighboursIndexTable, int** cellsLUTs, gpuPair* cellNeighbours, + const Tracklet** tracklets, + const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -364,44 +399,61 @@ GPUg() void computeLayerCellNeighboursKernel( const int maxCellNeighbours = 1e2) { for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells; iCurrentCellIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (neighboursIndexTable[iCurrentCellIndex] == neighboursIndexTable[iCurrentCellIndex + 1]) { + continue; + } + } const auto& currentCellSeed{cellSeedArray[layerIndex][iCurrentCellIndex]}; const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; const int nextLayerFirstCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex]}; const int nextLayerLastCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex + 1]}; int foundNeighbours{0}; for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - CellSeed nextCellSeed{cellSeedArray[layerIndex + 1][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); - foundNeighbours++; neighboursIndexTable[iCurrentCellIndex]++; } else { cellNeighbours[neighboursIndexTable[iCurrentCellIndex] + foundNeighbours] = {iCurrentCellIndex, iNextCell}; foundNeighbours++; - // FIXME: this is prone to race conditions: check on level is not atomic const int currentCellLevel{currentCellSeed.getLevel()}; if (currentCellLevel >= nextCellSeed.getLevel()) { - cellSeedArray[layerIndex + 1][iNextCell].setLevel(currentCellLevel + 1); + atomicMax(cellSeedArray[layerIndex + 1][iNextCell].getLevelPtr(), currentCellLevel + 1); } } } } } -template -GPUg() void computeLayerCellsKernel( +template +GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -409,15 +461,21 @@ GPUg() void computeLayerCellsKernel( int** trackletsLUT, const int nTrackletsCurrent, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTs, + const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut) { - constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // Hardcoded here for the moment. + constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // FIXME: Hardcoded here for the moment. for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (cellsLUTs[layer][iCurrentTrackletIndex] == cellsLUTs[layer][iCurrentTrackletIndex + 1]) { + continue; + } + } const Tracklet& currentTracklet = tracklets[layer][iCurrentTrackletIndex]; const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; const int nextLayerFirstTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex]}; @@ -431,6 +489,9 @@ GPUg() void computeLayerCellsKernel( break; } const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; + if (deltaROF && currentTracklet.getSpanRof(nextTracklet) > deltaROF) { + continue; + } const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; if (deltaTanLambda / cellDeltaTanLambdaSigma < nSigmaCut) { @@ -454,7 +515,7 @@ GPUg() void computeLayerCellsKernel( break; } - if (!track.correctForMaterial(layerxX0[layer + iC], layerxX0[layer] * constants::Radl * constants::Rho, true)) { + if (!track.correctForMaterial(layerxX0[layer + iC], layerxX0[layer + iC] * constants::Radl * constants::Rho, true)) { break; } @@ -472,20 +533,20 @@ GPUg() void computeLayerCellsKernel( continue; } if constexpr (!initRun) { - new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; + new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; } ++foundCells; - if constexpr (initRun) { - cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; - } } } + if constexpr (initRun) { + cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; + } } } -template -GPUg() void computeLayerTrackletsMultiROFKernel( - const IndexTableUtils* utils, +template +GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( + const IndexTableUtils* utils, const uint8_t* multMask, const int layerIndex, const int startROF, @@ -514,39 +575,54 @@ GPUg() void computeLayerTrackletsMultiROFKernel( { const int phiBins{utils->getNphiBins()}; const int zBins{utils->getNzBins()}; + const int tableSize{phiBins * zBins + 1}; for (unsigned int iROF{blockIdx.x}; iROF < endROF - startROF; iROF += gridDim.x) { - const short rof0 = iROF + startROF; - auto primaryVertices = getPrimaryVertices(rof0, rofPV, totalROFs, multMask, vertices); + 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())}; - const short minROF = o2::gpu::CAMath::Max(startROF, static_cast(rof0 - deltaROF)); - const short maxROF = o2::gpu::CAMath::Min(endROF - 1, static_cast(rof0 + deltaROF)); - auto clustersCurrentLayer = getClustersOnLayer(rof0, totalROFs, layerIndex, ROFClusters, clusters); + 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}; - auto currentCluster{clustersCurrentLayer[currentClusterIndex]}; - const int currentSortedIndex{ROFClusters[layerIndex][rof0] + currentClusterIndex}; + const auto& currentCluster{clustersCurrentLayer[currentClusterIndex]}; + const int currentSortedIndex{ROFClusters[layerIndex][pivotROF] + currentClusterIndex}; if (usedClusters[layerIndex][currentCluster.clusterId]) { continue; } + if constexpr (!initRun) { + if (trackletsLUT[layerIndex][currentSortedIndex] == trackletsLUT[layerIndex][currentSortedIndex + 1]) { + continue; + } + } const float inverseR0{1.f / currentCluster.radius}; for (int iV{startVtx}; iV < endVtx; ++iV) { auto& primaryVertex{primaryVertices[iV]}; - if (primaryVertex.isFlagSet(2) && iteration != 3) { + if ((primaryVertex.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !primaryVertex.isFlagSet(Vertex::Flags::UPCMode))) { continue; } + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(resolutionPV) / primaryVertex.getNContributors() + math_utils::Sq(positionResolution)); const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution + 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)}; + 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; } @@ -556,9 +632,8 @@ GPUg() void computeLayerTrackletsMultiROFKernel( phiBinsNum += phiBins; } - const int tableSize{phiBins * zBins + 1}; - for (short rof1{minROF}; rof1 <= maxROF; ++rof1) { - auto clustersNextLayer = getClustersOnLayer(rof1, totalROFs, layerIndex + 1, ROFClusters, clusters); + for (short targetROF{minROF}; targetROF <= maxROF; ++targetROF) { + auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs, layerIndex + 1, ROFClusters, clusters); if (clustersNextLayer.empty()) { continue; } @@ -566,8 +641,8 @@ GPUg() void computeLayerTrackletsMultiROFKernel( int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTables[layerIndex + 1][(rof1 - startROF) * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTables[layerIndex + 1][(rof1 - startROF) * tableSize + maxBinIndex]; + 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; @@ -578,14 +653,14 @@ GPUg() void computeLayerTrackletsMultiROFKernel( } 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)}; - const int nextSortedIndex{ROFClusters[layerIndex + 1][rof1] + nextClusterIndex}; 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)}; - new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, rof0, rof1}; + const int nextSortedIndex{ROFClusters[layerIndex + 1][targetROF] + nextClusterIndex}; + new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, pivotROF, targetROF}; } ++storedTracklets; } @@ -597,37 +672,43 @@ GPUg() void computeLayerTrackletsMultiROFKernel( } } -template -GPUg() void compileTrackletsLookupTableKernel(const Tracklet* tracklets, - int* trackletsLookUpTable, - const int nTracklets) +GPUg() void __launch_bounds__(256, 1) compileTrackletsLookupTableKernel( + const Tracklet* tracklets, + int* trackletsLookUpTable, + const int nTracklets) { for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { atomicAdd(&trackletsLookUpTable[tracklets[currentTrackletIndex].firstClusterIndex], 1); } } -template -GPUg() void processNeighboursKernel(const int layer, - const int level, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, - const int* currentCellIds, - const unsigned int nCurrentCells, - CellSeed* updatedCellSeeds, - int* updatedCellsIds, - int* foundSeedsTable, // auxiliary only in GPU code to compute the number of cells per iteration - const unsigned char** usedClusters, // Used clusters - int* neighbours, - int* neighboursLUT, - const TrackingFrameInfo** foundTrackingFrameInfo, - const float bz, - const float maxChi2ClusterAttachment, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType) +template +GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( + const int layer, + const int level, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellIds, + const unsigned int nCurrentCells, + CellSeed* updatedCellSeeds, + int* updatedCellsIds, + int* foundSeedsTable, // auxiliary only in GPU code to compute the number of cells per iteration + const unsigned char** usedClusters, // Used clusters + int* neighbours, + int* neighboursLUT, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float bz, + const float maxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) { - constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // Hardcoded here for the moment. + 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) { @@ -645,7 +726,7 @@ GPUg() void processNeighboursKernel(const int layer, for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { const int neighbourCellId = neighbours[iNeighbourCell]; - const CellSeed& neighbourCell = allCellSeeds[layer - 1][neighbourCellId]; + const auto& neighbourCell = allCellSeeds[layer - 1][neighbourCellId]; if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { continue; @@ -656,7 +737,7 @@ GPUg() void processNeighboursKernel(const int layer, if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { continue; } - CellSeed seed{currentCell}; + auto seed{currentCell}; auto& trHit = foundTrackingFrameInfo[layer - 1][neighbourCell.getFirstClusterIndex()]; if (!seed.rotate(trHit.alphaTrackingFrame)) { @@ -681,13 +762,13 @@ GPUg() void processNeighboursKernel(const int layer, if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { continue; } - seed.getClusters()[layer - 1] = neighbourCell.getFirstClusterIndex(); - seed.setLevel(neighbourCell.getLevel()); - seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); - seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); 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; } @@ -696,175 +777,12 @@ GPUg() void processNeighboursKernel(const int layer, } } -///////////////////////////////////////// -// Debug Kernels -///////////////////////////////////////// - -template -GPUd() void pPointer(T* ptr) -{ - printf("[%p]\t", ptr); -} - -template -GPUg() void printPointersKernel(std::tuple args) -{ - auto print_all = [&](auto... ptrs) { - (pPointer(ptrs), ...); - }; - std::apply(print_all, args); -} - -template -struct trackletSortEmptyFunctor { - GPUhd() bool operator()(const T& lhs, const T& rhs) const - { - return lhs.firstClusterIndex > rhs.firstClusterIndex; - } -}; - -template -struct trackletSortIndexFunctor { - GPUhd() bool operator()(const T& lhs, const T& rhs) const - { - return lhs.firstClusterIndex < rhs.firstClusterIndex || (lhs.firstClusterIndex == rhs.firstClusterIndex && lhs.secondClusterIndex < rhs.secondClusterIndex); - } -}; - -GPUg() void printBufferLayerOnThread(const int layer, const int* v, unsigned int 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"); - } -} - -GPUg() void printMatrixRow(const int row, int** mat, const unsigned int rowLength, const int len = 150, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - for (int i{0}; i < rowLength; ++i) { - if (!(i % len)) { - printf("\n row %d: ===> %d/%d\t", row, i, (int)rowLength); - } - printf("%d\t", mat[row][i]); - } - printf("\n"); - } -} - -GPUg() void printBufferPointersLayerOnThread(const int layer, void** v, unsigned int 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("%p\t", (void*)v[i]); - } - printf("\n"); - } -} - -GPUg() void printVertices(const Vertex* v, unsigned int size, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vertices: \n"); - for (int i{0}; i < size; ++i) { - printf("\tx=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); - } - } -} - -GPUg() void printNeighbours(const gpuPair* neighbours, - const int* nNeighboursIndexTable, - const unsigned int nCells, - const unsigned int tId = 0) -{ - for (unsigned int iNeighbour{0}; iNeighbour < nNeighboursIndexTable[nCells]; ++iNeighbour) { - if (threadIdx.x == tId) { - printf("%d -> %d\n", neighbours[iNeighbour].first, neighbours[iNeighbour].second); - } - } -} - -GPUg() void printTrackletsLUTPerROF(const int layerId, - const int** ROFClusters, - int** luts, - const int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - for (auto rofId{0}; rofId < 2304; ++rofId) { - int nClus = ROFClusters[layerId][rofId + 1] - ROFClusters[layerId][rofId]; - if (!nClus) { - continue; - } - printf("rof: %d (%d) ==> ", rofId, nClus); - - for (int iC{0}; iC < nClus; ++iC) { - int nT = luts[layerId][ROFClusters[layerId][rofId] + iC]; - printf("%d\t", nT); - } - printf("\n"); - } - } -} - -GPUg() void printCellSeeds(CellSeed* seed, int nCells, const unsigned int tId = 0) -{ - for (unsigned int iCell{0}; iCell < nCells; ++iCell) { - if (threadIdx.x == tId) { - seed[iCell].printCell(); - } - } -} - -template -GPUhi() void cubExclusiveScanInPlace(T* in_out, int num_items, cudaStream_t stream = nullptr) -{ - void* d_temp_storage = nullptr; - size_t temp_storage_bytes = 0; - GPUChkErrS(cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, in_out, - in_out, num_items, stream)); - GPUChkErrS(cudaMalloc(&d_temp_storage, temp_storage_bytes)); - GPUChkErrS(cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, in_out, - in_out, num_items, stream)); - GPUChkErrS(cudaFree(d_temp_storage)); -} - -template -GPUhi() void cubExclusiveScanInPlace(Vector& in_out, int num_items, cudaStream_t stream = nullptr) -{ - cubExclusiveScanInPlace(thrust::raw_pointer_cast(in_out.data()), num_items, stream); -} - -template -GPUhi() void cubInclusiveScanInPlace(T* in_out, int num_items, cudaStream_t stream = nullptr) -{ - void* d_temp_storage = nullptr; - size_t temp_storage_bytes = 0; - GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in_out, - in_out, num_items, stream)); - GPUChkErrS(cudaMalloc(&d_temp_storage, temp_storage_bytes)); - GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in_out, - in_out, num_items, stream)); - GPUChkErrS(cudaFree(d_temp_storage)); -} - -template -GPUhi() void cubInclusiveScanInPlace(Vector& in_out, int num_items, cudaStream_t stream = nullptr) -{ - cubInclusiveScanInPlace(thrust::raw_pointer_cast(in_out.data()), num_items, stream); -} } // namespace gpu template -void countTrackletsInROFsHandler(const IndexTableUtils* utils, +void countTrackletsInROFsHandler(const IndexTableUtils* utils, const uint8_t* multMask, + const int layer, const int startROF, const int endROF, const int maxROF, @@ -889,45 +807,46 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, const int nBlocks, - const int nThreads) + const int nThreads, + gpu::Streams& streams) { - for (int iLayer = 0; iLayer < nLayers - 1; ++iLayer) { - gpu::computeLayerTrackletsMultiROFKernel<<>>( - utils, - multMask, - iLayer, - startROF, - endROF, - maxROF, - deltaROF, - vertices, - rofPV, - nVertices, - vertexId, - clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - nullptr, - trackletsLUTs, - iteration, - NSigmaCut, - phiCuts[iLayer], - resolutionPV, - minRs[iLayer + 1], - maxRs[iLayer + 1], - resolutions[iLayer], - radii[iLayer + 1] - radii[iLayer], - mulScatAng[iLayer]); - gpu::cubExclusiveScanInPlace(trackletsLUTsHost[iLayer], nClusters[iLayer] + 1); - } + 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, +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const uint8_t* multMask, + const int layer, const int startROF, const int endROF, const int maxROF, @@ -955,50 +874,54 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, const int nBlocks, - const int nThreads) + const int nThreads, + gpu::Streams& streams) { - for (int iLayer = 0; iLayer < nLayers - 1; ++iLayer) { - gpu::computeLayerTrackletsMultiROFKernel<<>>(utils, - multMask, - iLayer, - startROF, - endROF, - maxROF, - deltaROF, - vertices, - rofPV, - nVertices, - vertexId, - clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - tracklets, - trackletsLUTs, - iteration, - NSigmaCut, - phiCuts[iLayer], - resolutionPV, - minRs[iLayer + 1], - maxRs[iLayer + 1], - resolutions[iLayer], - radii[iLayer + 1] - radii[iLayer], - mulScatAng[iLayer]); - thrust::device_ptr tracklets_ptr(spanTracklets[iLayer]); - thrust::sort(thrust::device, tracklets_ptr, tracklets_ptr + nTracklets[iLayer], gpu::sort_tracklets()); - auto unique_end = thrust::unique(thrust::device, tracklets_ptr, tracklets_ptr + nTracklets[iLayer], gpu::equal_tracklets()); - nTracklets[iLayer] = unique_end - tracklets_ptr; - if (iLayer > 0) { - GPUChkErrS(cudaMemset(trackletsLUTsHost[iLayer], 0, nClusters[iLayer] * sizeof(int))); - gpu::compileTrackletsLookupTableKernel<<>>( - spanTracklets[iLayer], trackletsLUTsHost[iLayer], nTracklets[iLayer]); - gpu::cubExclusiveScanInPlace(trackletsLUTsHost[iLayer], nClusters[iLayer] + 1); - } + 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, @@ -1007,18 +930,20 @@ void countCellsHandler( int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + 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) + const int nThreads, + gpu::Streams& streams) { - gpu::computeLayerCellsKernel<<>>( + gpu::computeLayerCellsKernel<<>>( sortedClusters, // const Cluster** unsortedClusters, // const Cluster** tfInfo, // const TrackingFrameInfo** @@ -1028,13 +953,16 @@ void countCellsHandler( layer, // const int cells, // CellSeed* cellsLUTsArrayDevice, // int** + deltaROF, // const int bz, // const float maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float nSigmaCut); // const float - gpu::cubExclusiveScanInPlace(cellsLUTsHost, nTracklets + 1); + 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, @@ -1043,18 +971,19 @@ void computeCellsHandler( int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + 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) + const int nThreads, + gpu::Streams& streams) { - gpu::computeLayerCellsKernel<<>>( + gpu::computeLayerCellsKernel<<>>( sortedClusters, // const Cluster** unsortedClusters, // const Cluster** tfInfo, // const TrackingFrameInfo** @@ -1064,51 +993,58 @@ void computeCellsHandler( layer, // const int cells, // CellSeed* cellsLUTsArrayDevice, // int** + deltaROF, // const int bz, // const float maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float nSigmaCut); // const float } -unsigned int countCellNeighboursHandler(CellSeed** cellsLayersDevice, - int* neighboursLUT, - int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - 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) +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<<>>( + gpu::computeLayerCellNeighboursKernel<<>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, cellsLUTs, cellNeighbours, + tracklets, + deltaROF, maxChi2ClusterAttachment, bz, layerIndex, nCells, maxCellNeighbours); - - gpu::cubInclusiveScanInPlace(neighboursLUT, nCellsNext); - gpu::cubExclusiveScanInPlace(neighboursIndexTable, nCells + 1); - unsigned int nNeighbours; - GPUChkErrS(cudaMemcpy(&nNeighbours, &neighboursLUT[nCellsNext - 1], sizeof(unsigned int), cudaMemcpyDeviceToHost)); - return nNeighbours; + 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); } -void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, +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, @@ -1116,67 +1052,68 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, const int nBlocks, - const int nThreads) + const int nThreads, + gpu::Stream& stream) { - - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, cellsLUTs, cellNeighbours, + tracklets, + deltaROF, maxChi2ClusterAttachment, bz, layerIndex, nCells, maxCellNeighbours); - GPUChkErrS(cudaPeekAtLastError()); - GPUChkErrS(cudaDeviceSynchronize()); } 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(neighVectorPairs, neighVectorPairs + nNeigh, gpu::is_invalid_pair()); + auto updatedEnd = thrust::remove_if(nosync_policy, neighVectorPairs, neighVectorPairs + nNeigh, gpu::is_invalid_pair()); size_t newSize = updatedEnd - neighVectorPairs; - thrust::stable_sort(neighVectorPairs, neighVectorPairs + newSize, gpu::sort_by_second()); - thrust::transform(neighVectorPairs, neighVectorPairs + newSize, validNeighs, gpu::pair_to_first()); - + 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, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, std::array& nCells, const unsigned char** usedClusters, std::array& neighbours, gsl::span neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, - bounded_vector& seedsHost, - o2::its::ExternalAllocator* allocator, + 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) { - auto allocInt = gpu::TypedAllocator(allocator); - auto allocCellSeed = gpu::TypedAllocator(allocator); - thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); // Shortcut: device_vector skips central memory management, we are relying on the contingency. - // TODO: fix this. - - gpu::processNeighboursKernel<<>>( + 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, @@ -1194,12 +1131,11 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - gpu::cubExclusiveScanInPlace(foundSeedsTable, nCells[startLayer] + 1); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); - thrust::device_vector> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); - gpu::processNeighboursKernel<<>>( + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); + gpu::processNeighboursKernel<<>>( startLayer, startLevel, allCellSeeds, @@ -1217,21 +1153,21 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); int level = startLevel; thrust::device_vector> lastCellId(allocInt); - thrust::device_vector> lastCellSeed(allocCellSeed); + 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>(allocCellSeed).swap(updatedCellSeed); + thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); auto lastCellSeedSize{lastCellSeed.size()}; foundSeedsTable.resize(lastCellSeedSize + 1); - thrust::fill(foundSeedsTable.begin(), foundSeedsTable.end(), 0); + thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( iLayer, --level, allCellSeeds, @@ -1249,16 +1185,15 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - gpu::cubExclusiveScanInPlace(foundSeedsTable, foundSeedsTable.size()); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); auto foundSeeds{foundSeedsTable.back()}; updatedCellId.resize(foundSeeds); - thrust::fill(updatedCellId.begin(), updatedCellId.end(), 0); + thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); - thrust::fill(updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( iLayer, level, allCellSeeds, @@ -1277,50 +1212,115 @@ void processNeighboursHandler(const int startLayer, propagator, matCorrType); } - thrust::device_vector> outSeeds(updatedCellSeed.size(), allocCellSeed); - auto end = thrust::copy_if(updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + 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); } -void trackSeedHandler(CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - o2::its::TrackITSExt* tracks, - std::vector& minPtsHost, - const unsigned int nSeeds, - const float bz, - const int startLevel, - float maxChi2ClusterAttachment, - float maxChi2NDF, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType, - 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) { + // TODO: the minPts&layerRadii is transfered twice + // we should allocate this in constant memory and stop these + // small transferes! thrust::device_vector minPts(minPtsHost); - gpu::fitTrackSeedsKernel<<>>( - trackSeeds, // CellSeed* - foundTrackingFrameInfo, // TrackingFrameInfo** - tracks, // TrackITSExt* - thrust::raw_pointer_cast(&minPts[0]), // const float* minPts, - nSeeds, // const unsigned int - bz, // const float - startLevel, // const int - maxChi2ClusterAttachment, // float - maxChi2NDF, // float - propagator, // const o2::base::Propagator* - matCorrType); // o2::base::PropagatorF::MatCorrType - thrust::device_ptr tr_ptr(tracks); + 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); +} - thrust::sort(tr_ptr, tr_ptr + nSeeds, gpu::compare_track_chi2()); - GPUChkErrS(cudaPeekAtLastError()); - GPUChkErrS(cudaDeviceSynchronize()); +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) +{ + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + tracks, // TrackITSExt* + seedLUT, // const int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::device_ptr tr_ptr(tracks); + thrust::sort(sync_policy, tr_ptr, tr_ptr + nTracks, gpu::compare_track_chi2()); } -template void countTrackletsInROFsHandler<7>(const IndexTableUtils* utils, +/// 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, @@ -1345,11 +1345,14 @@ template void countTrackletsInROFsHandler<7>(const IndexTableUtils* utils, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, const int nBlocks, - const int nThreads); + const int nThreads, + gpu::Streams& streams); -template void computeTrackletsInROFsHandler<7>(const IndexTableUtils* utils, +template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, const uint8_t* multMask, + const int layer, const int startROF, const int endROF, const int maxROF, @@ -1377,25 +1380,144 @@ template void computeTrackletsInROFsHandler<7>(const IndexTableUtils* utils, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, const int nBlocks, - const int nThreads); + 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** allCellSeeds, - CellSeed* currentCellSeeds, + 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, - o2::its::ExternalAllocator*, + 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/VertexerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx index 90d654a26a43d..658d3cf0dfb91 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx @@ -11,217 +11,169 @@ // /// \author matteo.concas@cern.ch -#include -#include -#include -#include -#include -#include - -#ifdef VTX_DEBUG -#include "TTree.h" -#include "TFile.h" -#endif +#include +#include "ITStracking/TrackingConfigParam.h" #include "ITStrackingGPU/VertexingKernels.h" #include "ITStrackingGPU/VertexerTraitsGPU.h" namespace o2::its { -void VertexerTraitsGPU::initialise(const TrackingParameters& trackingParams, const int iteration) +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->initialise(0, trackingParams, 3, &mIndexTableUtils, &mTfGPUParams); + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); } -void VertexerTraitsGPU::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) +template +void VertexerTraitsGPU::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) { - mVrtParams = vrtPar; + this->mVrtParams = vrtPar; mTfGPUParams = tfPar; - mIndexTableUtils.setTrackingParameters(vrtPar[0]); - for (auto& par : mVrtParams) { - par.phiSpan = static_cast(std::ceil(mIndexTableUtils.getNphiBins() * par.phiCut / o2::constants::math::TwoPI)); - par.zSpan = static_cast(std::ceil(par.zCut * mIndexTableUtils.getInverseZCoordinate(0))); + 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))); } } -void VertexerTraitsGPU::computeTracklets(const int iteration) +template +void VertexerTraitsGPU::computeTracklets(const int iteration) { - if (!mTimeFrameGPU->getClusters().size()) { + if (mTimeFrameGPU->getClusters().empty()) { 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); - // } + 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()); } -void VertexerTraitsGPU::computeTrackletMatching(const int iteration) +template +void VertexerTraitsGPU::computeTrackletMatching(const int iteration) { -} + if (!mTimeFrameGPU->getTotalTrackletsTF(0) || !mTimeFrameGPU->getTotalTrackletsTF(1)) { + return; + } -void VertexerTraitsGPU::computeVertices(const int iteration) -{ + 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()); } -void VertexerTraitsGPU::computeVerticesHist() +template +void VertexerTraitsGPU::computeVertices(const int iteration) { + LOGP(fatal, "This step is not implemented yet!"); + mTimeFrameGPU->loadUsedClustersDevice(); } -VertexerTraits* createVertexerTraitsGPU() -{ - return new VertexerTraitsGPU; -} +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 index 126e799efce5d..a2787bb13598d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu @@ -14,364 +14,454 @@ #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 -{ -namespace its +namespace o2::its { -using math_utils::getNormalizedPhi; namespace gpu { -template -void trackletFinderHandler(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 unsigned int maxTrackletsPerCluster, - const int nBlocks, - const int nThreads) -{ - gpu::trackleterKernelMultipleRof<<>>( - clustersNextLayer, // const Cluster* clustersNextLayer, // 0 2 - clustersCurrentLayer, // const Cluster* clustersCurrentLayer, // 1 1 - sizeNextLClusters, // const int* sizeNextLClusters, - sizeCurrentLClusters, // const int* sizeCurrentLClusters, - nextIndexTables, // const int* nextIndexTables, - Tracklets, // Tracklet* Tracklets, - foundTracklets, // int* foundTracklets, - utils, // const IndexTableUtils* utils, - startRofId, // const unsigned int startRofId, - rofSize, // const unsigned int rofSize, - phiCut, // const float phiCut, - maxTrackletsPerCluster); // const unsigned int maxTrackletsPerCluster = 1e2 -} -/* - -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)}; -} - -template -GPUd() void printOnThread(const unsigned int tId, const char* str, Args... args) +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) { - 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, unsigned int 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]); + 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; } - printf("\n"); - } -} - -GPUg() void printBufferOnThreadF(const float* v, unsigned int 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]); + 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; } - 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 unsigned int 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 short rofId, - const unsigned int 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 unsigned int stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - const Cluster& currentCluster = clustersCurrentLayer[iCurrentLayerClusterIndex]; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, *utils)}; + 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; } - // 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}; + 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; } - ++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); + if constexpr (dryRun) { + for (int32_t i{0}; i < (int32_t)((2 * deltaRof) + 1); ++i) { + trackletsPerCluster[iCurrentLayerClusterIndex] += storedTrackletsLocal[i]; + } } } } } -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 short startRofId, - const short rofSize, - const float phiCut, - const unsigned int maxTrackletsPerCluster = 1e2) +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) { - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (auto iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - short rof = static_cast(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]); + 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); - // single rof loop on layer1 clusters - for (int iCurrentLayerClusterIndex = threadIdx.x; iCurrentLayerClusterIndex < nClustersCurrentLayerRof; iCurrentLayerClusterIndex += blockDim.x) { - unsigned int storedTracklets{0}; - const unsigned int 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, rof, rof}; - } else { - new (TrackletsRof + stride + storedTracklets) Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, rof, rof}; - } - ++storedTracklets; - } + 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; } } } - 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); - // } + + if constexpr (dryRun) { + linesPerCluster[iCurrentLayerClusterIndex] = validTracklets; + } } } } -template -GPUg() void trackletSelectionKernelSingleRof( - const Cluster* clusters0, - const Cluster* clusters1, - const unsigned int 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) +template +GPUg() void compileTrackletsPerROFKernel(const int32_t nRofs, + int** GPUrestrict() nTrackletsPerROF, + const int32_t** GPUrestrict() rofClusters, + const int32_t** GPUrestrict() nTrackletsPerCluster) { - 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; - } - } + // 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]; } - 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); + 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 -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 +template +GPUhi() void cubExclusiveScan(const T* GPUrestrict() in, T* GPUrestrict() out, int32_t num_items, cudaStream_t stream) { - 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 + 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 @@ -567,6 +657,4 @@ GPUg() void computeVertexKernel( } } */ -} // namespace gpu -} // namespace its -} // namespace o2 +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index cc43b6845a714..a40aac491a386 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -12,6 +12,9 @@ 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/TimeFrameGPU.cu @@ -28,4 +31,4 @@ if(HIP_ENABLED) hip::host PRIVATE_LINK_LIBRARIES O2::GPUTrackingHIPExternalProvider TARGETVARNAME targetName) -endif() \ No newline at end of file +endif() diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h deleted file mode 100644 index 971ae6a7fe83a..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file ArrayUtils.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ -#define TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ - -#include -#include -#include - -namespace o2 -{ -namespace its -{ -namespace CA -{ - -namespace ArrayUtils -{ -template -constexpr std::array fillArray(Initializer, std::index_sequence); -template -constexpr std::array fillArray(Initializer); -} // namespace ArrayUtils - -template -constexpr std::array ArrayUtils::fillArray(Initializer initializer, std::index_sequence) -{ - return std::array{{initializer(Is)...}}; -} - -template -constexpr std::array ArrayUtils::fillArray(Initializer initializer) -{ - return ArrayUtils::fillArray(initializer, std::make_index_sequence{}); -} -} // namespace CA -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h index eced0c64c73a5..66634c1a07eea 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h @@ -22,6 +22,8 @@ #include #include +#include "ITStracking/ExternalAllocator.h" + #include "GPUCommonLogger.h" namespace o2::its @@ -56,6 +58,7 @@ class BoundedMemoryResource final : public std::pmr::memory_resource 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 { @@ -69,7 +72,14 @@ class BoundedMemoryResource final : public std::pmr::memory_resource } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, std::memory_order_acq_rel, std::memory_order_relaxed)); - return mUpstream->allocate(bytes, alignment); + 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 @@ -87,11 +97,12 @@ class BoundedMemoryResource final : public std::pmr::memory_resource size_t getMaxMemory() const noexcept { return mMaxMemory; } void setMaxMemory(size_t max) { - if (mUsedMemory > max) { + size_t used = mUsedMemory.load(std::memory_order_acquire); + if (used > max) { ++mCountThrow; - throw MemoryLimitExceeded(0, mUsedMemory, max); + throw MemoryLimitExceeded(0, used, max); } - mMaxMemory = max; + mMaxMemory.store(max, std::memory_order_release); } void print() const @@ -106,74 +117,80 @@ class BoundedMemoryResource final : public std::pmr::memory_resource } private: - size_t mMaxMemory{std::numeric_limits::max()}; + std::atomic mMaxMemory{std::numeric_limits::max()}; std::atomic mCountThrow{0}; std::atomic mUsedMemory{0}; - std::pmr::memory_resource* mUpstream; + std::unique_ptr mAdaptor{nullptr}; + std::pmr::memory_resource* mUpstream{nullptr}; }; template using bounded_vector = std::pmr::vector; template -void deepVectorClear(std::vector& vec) +inline void deepVectorClear(std::vector& vec) { std::vector().swap(vec); } template -inline void deepVectorClear(bounded_vector& vec, BoundedMemoryResource* bmr = nullptr) +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(); - if (bmr == nullptr) { - auto alloc = vec.get_allocator().resource(); - new (&vec) bounded_vector(alloc); - } else { - new (&vec) bounded_vector(bmr); - } + new (&vec) bounded_vector(std::pmr::polymorphic_allocator{tmr}); } template -void deepVectorClear(std::vector>& vec, BoundedMemoryResource* bmr = nullptr) +inline void deepVectorClear(std::vector>& vec, std::pmr::memory_resource* mr = nullptr) { for (auto& v : vec) { - deepVectorClear(v, bmr); + deepVectorClear(v, mr); } } template -void deepVectorClear(std::array, S>& arr, BoundedMemoryResource* bmr = nullptr) +inline void deepVectorClear(std::array, S>& arr, std::pmr::memory_resource* mr = nullptr) { for (size_t i{0}; i < S; ++i) { - deepVectorClear(arr[i], bmr); + deepVectorClear(arr[i], mr); } } template -void clearResizeBoundedVector(bounded_vector& vec, size_t size, BoundedMemoryResource* bmr, T def = T()) +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(size, def, bmr); + new (&vec) bounded_vector(sz, def, std::pmr::polymorphic_allocator{tmr}); } template -void clearResizeBoundedVector(std::vector>& vec, size_t size, BoundedMemoryResource* bmr) +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(bmr); + for (size_t i = 0; i < size; ++i) { + vec.emplace_back(std::pmr::polymorphic_allocator>{mr}); } } template -void clearResizeBoundedArray(std::array, S>& arr, size_t size, BoundedMemoryResource* bmr, T def = T()) +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, bmr, def); + 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 fc3656aef800c..902092a510eb0 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -15,12 +15,8 @@ #ifndef TRACKINGITSU_INCLUDE_CACELL_H_ #define TRACKINGITSU_INCLUDE_CACELL_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#include -#endif +#include "ITStracking/Constants.h" #include "GPUCommonDef.h" namespace o2::its @@ -29,22 +25,6 @@ namespace o2::its class Cell final { public: - GPUhdDefault() Cell() = default; - GPUhd() 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) {} - GPUhdDefault() Cell(const Cell&) = default; - GPUhdDefault() Cell(Cell&&) = default; - GPUhdDefault() ~Cell() = default; - - GPUhdDefault() Cell& operator=(const Cell&) = default; - GPUhdDefault() Cell& operator=(Cell&&) noexcept = default; - GPUhd() int getFirstClusterIndex() const { return mFirstClusterIndex; }; GPUhd() int getSecondClusterIndex() const { return mSecondClusterIndex; }; GPUhd() int getThirdClusterIndex() const { return mThirdClusterIndex; }; @@ -55,20 +35,22 @@ class Cell final GPUhd() int* getLevelPtr() { return &mLevel; } private: - int mFirstClusterIndex{0}; - int mSecondClusterIndex{0}; - int mThirdClusterIndex{0}; - int mFirstTrackletIndex{0}; - int mSecondTrackletIndex{0}; - int mLevel{0}; + int mFirstClusterIndex{constants::UnusedIndex}; + int mSecondClusterIndex{constants::UnusedIndex}; + int mThirdClusterIndex{constants::UnusedIndex}; + int mFirstTrackletIndex{constants::UnusedIndex}; + int mSecondTrackletIndex{constants::UnusedIndex}; + int mLevel{constants::UnusedIndex}; }; +template class CellSeed final : public o2::track::TrackParCovF { public: GPUhdDefault() CellSeed() = default; GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(1) { + mClusters.fill(constants::UnusedIndex); setUserField(innerL); mClusters[innerL + 0] = cl0; mClusters[innerL + 1] = cl1; @@ -94,18 +76,25 @@ class CellSeed final : public o2::track::TrackParCovF 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("trkl: %d, %d\t lvl: %d\t chi2: %f\n", mTracklets[0], mTracklets[1], mLevel, mChi2); + printf("cell: %d, %d\t lvl: %d\t chi2: %f\tcls: [", mTracklets[0], mTracklets[1], mLevel, mChi2); + for (int i = 0; i < nLayers; ++i) { + printf("%d", mClusters[i]); + if (i < nLayers - 1) { + printf(" | "); + } + } + printf("]\n"); } private: - float mChi2 = 0.f; - int mLevel = 0; - int mTracklets[2] = {-1, -1}; - int mClusters[7] = {-1, -1, -1, -1, -1, -1, -1}; + float mChi2 = -999.f; + int mLevel = constants::UnusedIndex; + std::array mTracklets = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index eaefbee5e2aaa..b96f0558943a6 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -16,38 +16,30 @@ #ifndef TRACKINGITSU_INCLUDE_CACLUSTER_H_ #define TRACKINGITSU_INCLUDE_CACLUSTER_H_ +#include +#include "ITStracking/Constants.h" #include "GPUCommonRtypes.h" -#include "GPUCommonArray.h" namespace o2::its { +template class IndexTableUtils; struct Cluster final { GPUhdDefault() Cluster() = default; GPUhd() Cluster(const float x, const float y, const float z, const int idx); - GPUhd() Cluster(const int, const IndexTableUtils& utils, const Cluster&); - GPUhd() Cluster(const int, const float3&, const IndexTableUtils& utils, const Cluster&); + 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; - - // TODO - /*GPUhdDefault() bool operator==(const Cluster&) const = default;*/ - GPUhd() bool operator==(const Cluster& other) const - { - return xCoordinate == other.xCoordinate && - yCoordinate == other.yCoordinate && - zCoordinate == other.zCoordinate && - phi == other.phi && - radius == other.radius && - clusterId == other.clusterId && - indexTableBinIndex == other.indexTableBinIndex; - } + GPUhdDefault() bool operator==(const Cluster&) const = default; GPUhd() void print() const; @@ -56,8 +48,8 @@ struct Cluster final { float zCoordinate{-999.f}; float phi{-999.f}; float radius{-999.f}; - int clusterId{-1}; - int indexTableBinIndex{-1}; + int clusterId{constants::UnusedIndex}; + int indexTableBinIndex{constants::UnusedIndex}; ClassDefNV(Cluster, 1); }; @@ -79,7 +71,7 @@ struct TrackingFrameInfo final { float zCoordinate{-999.f}; float xTrackingFrame{-999.f}; float alphaTrackingFrame{-999.f}; - std::array positionTrackingFrame = {-1., -1.}; + std::array positionTrackingFrame = {constants::UnusedIndex, constants::UnusedIndex}; std::array covarianceTrackingFrame = {999., 999., 999.}; ClassDefNV(TrackingFrameInfo, 1); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 3377b88e89069..0e7ad474ae455 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -15,25 +15,25 @@ #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::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*); 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]; } @@ -42,8 +42,9 @@ struct Line final { bool operator!=(const Line&) const; short getMinROF() const { return rof[0] < rof[1] ? rof[0] : rof[1]; } - float originPoint[3], cosinesDirector[3]; - float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; + 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 @@ -51,14 +52,10 @@ struct Line final { // 3 --> 1,1 // 4 --> 1,2 // 5 --> 2,2 - short rof[2]; -}; + short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; -GPUhdi() Line::Line() : weightMatrix{1., 0., 0., 1., 0., 1.} -{ - rof[0] = -1; - rof[1] = -1; -} + ClassDefNV(Line, 1); +}; GPUhdi() Line::Line(const Line& other) { @@ -66,32 +63,14 @@ 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]; - } + // for (int i{0}; i < 6; ++i) { + // weightMatrix[i] = other.weightMatrix[i]; + // } for (int i{0}; i < 2; ++i) { rof[i] = other.rof[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; - } - - rof[0] = -1; - rof[1] = -1; -} - GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) { originPoint[0] = innerClusters[tracklet.firstClusterIndex].xCoordinate; @@ -102,12 +81,10 @@ 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])}; - - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; - } + float inverseNorm{1.f / o2::gpu::CAMath::Hypot(cosinesDirector[0], cosinesDirector[1], cosinesDirector[2])}; + cosinesDirector[0] *= inverseNorm; + cosinesDirector[1] *= inverseNorm; + cosinesDirector[2] *= inverseNorm; rof[0] = tracklet.rof[0]; rof[1] = tracklet.rof[1]; @@ -130,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]) @@ -199,11 +167,7 @@ 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 @@ -243,7 +207,7 @@ class ClusterLines final 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 = -1; // rof + short mROF = constants::UnusedIndex; // rof }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 2a40b817a7005..10e1681c73e8d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -26,39 +26,12 @@ #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 { 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; @@ -78,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; @@ -92,8 +66,11 @@ struct TrackingParameters { o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; + 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}; - unsigned char StartLayerMask = 0x7F; + 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; @@ -107,6 +84,8 @@ struct TrackingParameters { float TrackFollowerNSigmaCutZ = 1.f; float TrackFollowerNSigmaCutPhi = 1.f; + bool createArtefactLabels{false}; + bool PrintMemory = false; // print allocator usage in epilog report size_t MaxMemory = std::numeric_limits::max(); bool DropTFUponFailure = false; @@ -140,6 +119,9 @@ struct VertexingParameters { 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(); @@ -166,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 48cc45e44cf1c..22642f2e23229 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -16,26 +16,39 @@ #ifndef TRACKINGITSU_INCLUDE_CONSTANTS_H_ #define TRACKINGITSU_INCLUDE_CONSTANTS_H_ +#include +#include + #include "ITStracking/Definitions.h" +#include "GPUCommonDefAPI.h" namespace o2::its::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; -constexpr float Tolerance{1e-12}; // numerical tolerance -constexpr int ClustersPerCell{3}; -constexpr int UnusedIndex{-1}; -constexpr float Resolution{0.0005f}; -constexpr float Radl = 9.36f; // Radiation length of Si [cm] -constexpr float Rho = 2.33f; // Density of Si [g/cm^3] -namespace its // to be removed +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] + +namespace helpers { -constexpr int UnusedIndex{-1}; -constexpr float Resolution{0.0005f}; -} // namespace its + +// initialize a std::array at compile time fully with T +template +constexpr std::array initArray() +{ + return [](std::index_sequence) { return std::array{(static_cast(Is), Value)...}; }(std::make_index_sequence{}); +} + +} // namespace helpers } // namespace o2::its::constants #endif /* TRACKINGITSU_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h index 352e13963b6d1..c3be0de2dade7 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h @@ -15,9 +15,9 @@ #ifndef TRACKINGITS_DEFINITIONS_H_ #define TRACKINGITS_DEFINITIONS_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#endif +#include + +#include "ReconstructionDataFormats/Vertex.h" #ifdef CA_DEBUG #define CA_DEBUGGER(x) x @@ -26,4 +26,20 @@ do { \ } while (0) #endif + +namespace o2::its +{ + +enum class TrackletMode { + Layer0Layer1 = 0, + Layer1Layer2 = 2 +}; + +using Vertex = o2::dataformats::Vertex>; + +template +using maybe_const = typename std::conditional::type; + +} // 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 1628bbc52776b..7d1e98736db2c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h @@ -16,15 +16,71 @@ #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* allocate(size_t) = 0; virtual void deallocate(char*, size_t) = 0; + virtual void* allocate(size_t) = 0; + void* allocate(size_t s, Type type) + { + auto old = mType; + mType = type; + void* p = allocate(s); + mType = old; + return p; + } + void* allocateStack(size_t s) + { + return allocate(s, (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + } + virtual void pushTagOnStack(uint64_t) = 0; + virtual void popTagOffStack(uint64_t) = 0; + + void setType(Type t) noexcept { mType = t; } + Type getType() const noexcept { return mType; } + + protected: + Type mType; }; + +class ExternalAllocatorAdaptor final : public std::pmr::memory_resource +{ + public: + explicit ExternalAllocatorAdaptor(ExternalAllocator* alloc) : mAlloc(alloc) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) override + { + void* p = mAlloc->allocate(bytes, o2::gpu::GPUMemoryResource::MemoryType::MEMORY_HOST); + if (!p) { + throw std::bad_alloc(); + } + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t) override + { + mAlloc->deallocate(static_cast(p), bytes); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override + { + return this == &other; + } + + private: + ExternalAllocator* mAlloc; +}; + } // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h index 0ada9dfbc6188..8adacdf58d74d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h @@ -16,62 +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; -} - template o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const itsmft::TopologyDictionary* dict, T& sig2y, T& sig2z) { @@ -115,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 61072cb2410b7..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,12 +51,13 @@ 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 / o2::constants::math::TwoPI; mNzBins = params.ZBins; @@ -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/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 e9cd306e63bc5..009f3a1b5b146 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h @@ -16,13 +16,15 @@ #ifndef TRACKINGCA_INCLUDE_ROAD_H #define TRACKINGCA_INCLUDE_ROAD_H +#include + #include "ITStracking/Constants.h" #include "GPUCommonDef.h" namespace o2::its { -template +template class Road final { public: @@ -45,14 +47,14 @@ class Road final 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; } @@ -60,8 +62,7 @@ class Road final } private: - int mCellIds[maxRoadSize]{constants::its::UnusedIndex}; - // int mLabel; + std::array mCellIds = constants::helpers::initArray(); unsigned char mRoadSize{0}; bool mIsFakeRoad{false}; }; 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 248e63aef382c..acc884ea68b8b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -33,7 +33,6 @@ #include "ITStracking/IndexTableUtils.h" #include "ITStracking/ExternalAllocator.h" #include "ITStracking/BoundedAllocator.h" - #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -62,35 +61,42 @@ namespace gpu template class TimeFrameGPU; } -using Vertex = o2::dataformats::Vertex>; template struct TimeFrame { + using IndexTableUtilsN = IndexTableUtils; + using CellSeedN = CellSeed; friend class gpu::TimeFrameGPU; - TimeFrame(); - virtual ~TimeFrame(); + + 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> 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 bounded_vector& vertices); void addPrimaryVerticesLabels(bounded_vector>& labels); - void addPrimaryVertices(const bounded_vector& vertices, const int rofId, const int iteration); - void addPrimaryVertices(const gsl::span& vertices, const int rofId, const int iteration); + 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; auto& getTotVertIteration() { return mTotVertPerIteration; } @@ -138,7 +144,7 @@ struct TimeFrame { 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(const bounded_vector& v) { mClusterSize = v; } + void setClusterSize(bounded_vector& v) { mClusterSize = std::move(v); } auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } auto& getCellsLabel(int layer) { return mCellLabels[layer]; } @@ -174,16 +180,16 @@ struct TimeFrame { 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); + void setMemoryPool(std::shared_ptr pool); auto& getMemoryPool() const noexcept { return mMemoryPool; } bool checkMemory(unsigned long max) { return getArtefactsMemory() < max; } unsigned long getArtefactsMemory() const; @@ -199,10 +205,8 @@ struct TimeFrame { void computeTracletsPerClusterScans(); int& getNTrackletsROF(int rofId, int combId) { return mNTrackletsPerROF[combId][rofId]; } auto& getLines(int rofId) { return mLines[rofId]; } - int getNLinesTotal() const - { - return std::accumulate(mLines.begin(), mLines.end(), 0, [](int sum, const auto& l) { return sum + l.size(); }); - } + 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); @@ -230,24 +234,17 @@ struct TimeFrame { 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(fatal, "External allocator is currently only supported for GPU"); - } - } - - ExternalAllocator* getExternalAllocator() { return mAllocator; } + /// 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*) - { - return; - }; + // Propagator const o2::base::PropagatorImpl* getDevicePropagator() const { return mPropagatorDevice; } + virtual void setDevicePropagator(const o2::base::PropagatorImpl*) {}; template void addClusterToLayer(int layer, T&&... args); @@ -255,9 +252,6 @@ struct TimeFrame { void addTrackingFrameInfoToLayer(int layer, T&&... args); void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } - void resetVectors(); - void resetTracklets(); - /// Debug and printing void checkTrackletLUTs(); void printROFoffsets(); @@ -269,9 +263,7 @@ struct TimeFrame { void printCellLUTs(); void printSliceInfo(const int, const int); - IndexTableUtils mIndexTableUtils; - - bool mIsGPU = false; + IndexTableUtilsN mIndexTableUtils; std::array, nLayers> mClusters; std::array, nLayers> mTrackingFrameInfo; @@ -290,14 +282,9 @@ struct TimeFrame { bounded_vector mROFramesPV; bounded_vector mPrimaryVertices; - // State if memory will be externally managed. - bool mExtAllocator = false; - ExternalAllocator* mAllocator = nullptr; - bool getExtAllocator() const { return mExtAllocator; } - std::array, nLayers> mUnsortedClusters; std::vector> mTracklets; - std::vector> mCells; + std::vector> mCells; bounded_vector> mRoads; std::vector> mTracks; std::vector> mCellsNeighbours; @@ -306,9 +293,13 @@ struct TimeFrame { const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU - void wipe(); + virtual void wipe(); + + // interface + virtual bool isGPU() const noexcept { return false; } + virtual const char* getName() const noexcept { return "CPU"; } - private: + protected: void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = nLayers); float mBz = 5.; unsigned int mNTotalLowPtVertices = 0; @@ -341,7 +332,9 @@ struct TimeFrame { 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 @@ -370,13 +363,30 @@ inline gsl::span> TimeFrame::getPri return {&(mVerticesMCRecInfo[start]), static_cast>::size_type>(delta)}; } +template +inline gsl::span TimeFrame::getPrimaryVerticesContributors(const int rofId) const +{ + // 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)}; +} + template inline gsl::span TimeFrame::getPrimaryVertices(int romin, int romax) const { if (mPrimaryVertices.empty()) { return {}; } - return {&mPrimaryVertices[mROFramesPV[romin]], static_cast::size_type>(mROFramesPV[romax + 1] - mROFramesPV[romin])}; + const int stop_idx = romax >= mNrof - 1 ? mNrof : romax + 1; + return {&mPrimaryVertices[mROFramesPV[romin]], static_cast::size_type>(mROFramesPV[stop_idx] - mROFramesPV[romin])}; } template @@ -511,8 +521,8 @@ inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) if (rofId < 0 || rofId >= mNrof) { return {}; } - return {&mIndexTables[layer][rofId * (mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1)], - static_cast::size_type>(mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1)}; + const int tableSize = mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1; + return {&mIndexTables[layer][rofId * tableSize], static_cast::size_type>(tableSize)}; } template @@ -573,7 +583,7 @@ inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofI template inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= mNrof || mTracklets[combId].empty()) { return {}; } auto startIdx{mNTrackletsPerROF[combId][rofId]}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index b393d743809fd..3ea382c626fed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -27,10 +27,11 @@ #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" @@ -50,32 +51,28 @@ class GPUChainITS; namespace its { +template class Tracker { - static constexpr int NLayers{7}; - using TrackerTraits7 = TrackerTraits; - using TimeFrame7 = TimeFrame; using LogFunc = std::function; public: - Tracker(TrackerTraits* traits); + Tracker(TrackerTraits* traits); - void adoptTimeFrame(TimeFrame& tf); + void adoptTimeFrame(TimeFrame& tf); void clustersToTracks( 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& p) { mTrkParams = p; } - void setMemoryPool(std::shared_ptr& pool) { mMemoryPool = pool; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector& getParameters() { return mTrkParams; } - void getGlobalConfiguration(); void setBz(float bz) { mTraits->setBz(bz); } - void setCorrType(const o2::base::PropagatorImpl::MatCorrType type) { mTraits->setCorrType(type); } bool isMatLUT() const { return mTraits->isMatLUT(); } - void setNThreads(int n) { mTraits->setNThreads(n); } - int getNThreads() const { return mTraits->getNThreads(); } + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } void printSummary() const; + void computeTracksMClabels(); private: void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } @@ -88,14 +85,13 @@ class Tracker // MC interaction void computeRoadsMClabels(); - void computeTracksMClabels(); void rectifyClusterIndices(); template float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); - TrackerTraits7* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame7* 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; @@ -117,8 +113,9 @@ class Tracker static constexpr std::array StateNames{"TimeFrame initialisation", "Tracklet finding", "Cell finding", "Neighbour finding", "Road finding"}; }; +template template -float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) +float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) { float diff{0.f}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 36956a5206277..ddc32ed18cbfe 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -16,17 +16,16 @@ #ifndef TRACKINGITSU_INCLUDE_TRACKERTRAITS_H_ #define TRACKINGITSU_INCLUDE_TRACKERTRAITS_H_ -#include +#include #include "DetectorsBase/Propagator.h" #include "ITStracking/Configuration.h" #include "ITStracking/MathUtils.h" +#include "ITStracking/IndexTableUtils.h" #include "ITStracking/TimeFrame.h" +#include "ITStracking/Cell.h" #include "ITStracking/BoundedAllocator.h" -#include -#include - // #define OPTIMISATION_OUTPUT namespace o2 @@ -43,6 +42,9 @@ template class TrackerTraits { public: + using IndexTableUtilsN = IndexTableUtils; + using CellSeedN = CellSeed; + virtual ~TrackerTraits() = default; virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); } @@ -58,18 +60,17 @@ class TrackerTraits virtual void findShortPrimaries(); virtual bool trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration); - virtual void processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId); + 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) { mTrkParams = trkPars; } TimeFrame* getTimeFrame() { return mTimeFrame; } virtual void setBz(float bz); float getBz() const { return mBz; } - void setCorrType(const o2::base::PropagatorImpl::MatCorrType type) { mCorrType = type; } 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; } + void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } auto getMemoryPool() const noexcept { return mMemoryPool; } // Others @@ -80,8 +81,8 @@ class TrackerTraits 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; } @@ -91,16 +92,15 @@ class TrackerTraits 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; - tbb::task_arena mTaskArena; + std::shared_ptr mTaskArena; protected: - o2::base::PropagatorImpl::MatCorrType mCorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; o2::gpu::GPUChainITS* mChain = nullptr; TimeFrame* mTimeFrame; std::vector mTrkParams; @@ -122,7 +122,7 @@ inline const int4 TrackerTraits::getBinsRect(const int layerIndex, floa return getEmptyBinsRect(); } - const IndexTableUtils& utils{mTimeFrame->mIndexTableUtils}; + 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, zRangeMax)), // /!\ trkParams can potentially change across iterations diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 039fe0756d6f6..0529bd53f2073 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -48,6 +48,9 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper::max(); @@ -92,20 +95,55 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::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 ITSGpuTrackingParamConfig : public o2::conf::ConfigurableParamHelper { - // GPU-specific parameters - int nBlocks = 20; - int nThreads = 256; + 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"); }; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h index 732efcb9e0861..a882ca9b779c4 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -24,17 +24,22 @@ #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 TrackerTraits7 = TrackerTraits; - using TimeFrame7 = TimeFrame; + using VertexerN = Vertexer; + using VertexerTraitsN = VertexerTraits; + using TrackerN = Tracker; + using TrackerTraitsN = TrackerTraits; + using TimeFrameN = TimeFrame; public: ITSTrackingInterface(bool isMC, @@ -59,45 +64,38 @@ class ITSTrackingInterface void initialise(); void run(framework::ProcessingContext& pc); void printSummary() const; - void end(); virtual void updateTimeDependentParams(framework::ProcessingContext& pc); virtual void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj); // Custom - void setTraitsFromProvider(VertexerTraits*, TrackerTraits7*, TimeFrame7*); - 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(); } - TimeFrame7* mTimeFrame = nullptr; + TimeFrameN* mTimeFrame = nullptr; protected: - virtual void loadROF(gsl::span& trackROFspan, + virtual void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels); - void getConfiguration(framework::ProcessingContext& pc); 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; + 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 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h index e0ae23c8bedde..e6c9db55198a3 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h @@ -16,57 +16,51 @@ #ifndef TRACKINGITS_INCLUDE_TRACKLET_H_ #define TRACKINGITS_INCLUDE_TRACKLET_H_ +#include "ITStracking/Constants.h" #include "ITStracking/Cluster.h" #include "GPUCommonRtypes.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" +#include "GPUCommonLogger.h" #ifndef GPUCA_GPUCODE_DEVICE +#ifndef GPU_NO_FMT #include +#include +#endif #endif namespace o2::its { struct Tracklet final { - GPUhdi() Tracklet(); + 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); - GPUhdi() bool operator==(const Tracklet&) const; - GPUhdi() bool operator!=(const Tracklet&) const; + GPUhdDefault() bool operator==(const Tracklet&) const = default; GPUhdi() unsigned char isEmpty() const { return firstClusterIndex < 0 || secondClusterIndex < 0; } + 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() void dump(); - GPUhdi() void dump() const; - GPUhdi() void dump(const int, const int); - GPUhdi() void dump(const int, const int) const; + 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; -#ifndef GPUCA_GPUCODE_DEVICE - std::string asString() const + GPUhd() void print() const { - return "fClIdx: " + std::to_string(firstClusterIndex) + " sClIdx: " + std::to_string(secondClusterIndex) + - " rof1: " + std::to_string(rof[0]) + " rof2: " + std::to_string(rof[1]) + " delta: " + std::to_string(getDeltaRof()); + 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); } -#endif - int firstClusterIndex; - int secondClusterIndex; - float tanLambda; - float phi; - short rof[2]; + int firstClusterIndex{constants::UnusedIndex}; + int secondClusterIndex{constants::UnusedIndex}; + float tanLambda{-999}; + float phi{-999}; + short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; ClassDefNV(Tracklet, 1); }; -GPUhdi() Tracklet::Tracklet() : firstClusterIndex{-1}, secondClusterIndex{-1}, tanLambda{0.0f}, phi{0.0f} -{ - rof[0] = -1; - rof[1] = -1; -} - GPUhdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, const Cluster& firstCluster, const Cluster& secondCluster, short rof0 = -1, short rof1 = -1) : firstClusterIndex{firstClusterOrderingIndex}, @@ -90,24 +84,6 @@ GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float ph // 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()) { @@ -116,26 +92,6 @@ GPUhdi() unsigned char Tracklet::operator<(const Tracklet& t) const return true; } -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 o2::its #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 63dd41b4a0a8f..d66bcd6ee2358 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -21,67 +21,72 @@ #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 "ITStracking/BoundedAllocator.h" -#include "ReconstructionDataFormats/Vertex.h" - -#include "ITStracking/ClusterLines.h" -#include "ITStracking/Tracklet.h" -#include "ITStracking/Cluster.h" - -#include "GPUCommonLogger.h" namespace o2::its { -using Vertex = o2::dataformats::Vertex>; - +template class Vertexer { - static constexpr int NLayers{7}; - using TimeFrame7 = TimeFrame; + 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(TimeFrame7& tf); + 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 getGlobalConfiguration(); - void setMemoryPool(std::shared_ptr& pool) { mMemoryPool = pool; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector exportVertices(); - VertexerTraits* getTraits() const { return mTraits; }; + VertexerTraitsN* getTraits() const { return mTraits; }; float clustersToVertices(LogFunc = [](const std::string& s) { std::cout << s << '\n'; }); void filterMCTracklets(); template - void findTracklets(T&&... args); - void findTrivialMCTracklets(); + void findTracklets(T&&... args) + { + mTraits->computeTracklets(std::forward(args)...); + } template - void validateTracklets(T&&... args); + void validateTracklets(T&&... args) + { + mTraits->computeTrackletMatching(std::forward(args)...); + } template - void findVertices(T&&... args); - void findHistVertices(); + void findVertices(T&&... args) + { + mTraits->computeVertices(std::forward(args)...); + } + + void addTruthSeeds() { mTraits->addTruthSeedingVertices(); } template - void initialiseVertexer(T&&... args); + void initialiseVertexer(T&&... args) + { + mTraits->initialise(std::forward(args)...); + } template void initialiseTimeFrame(T&&... args); // Utils - void dumpTraits() { mTraits->dumpVertexerTraits(); } template float evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args); @@ -90,11 +95,13 @@ class Vertexer 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 - TimeFrame7* 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; @@ -104,38 +111,16 @@ class Vertexer Trackleting, Validating, Finding, + TruthSeeding, NStates, }; State mCurState{Init}; - static constexpr std::array StateNames{"Initialisation", "Tracklet finding", "Tracklet validation", "Vertex finding"}; + 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)...); -} - -template -inline void Vertexer::validateTracklets(T&&... args) -{ - mTraits->computeTrackletMatching(std::forward(args)...); -} - -template -inline void Vertexer::findVertices(T&&... args) -{ - mTraits->computeVertices(std::forward(args)...); -} - -template -float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) +float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) { float diff{0.f}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index e1e1d44e8ead9..b1422d66e12df 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -17,6 +17,7 @@ #define O2_ITS_TRACKING_VERTEXER_TRAITS_H_ #include +#include #include #include @@ -42,15 +43,11 @@ class MCCompLabel; namespace its { -enum class TrackletMode { - Layer0Layer1 = 0, - Layer1Layer2 = 2 -}; - +template class VertexerTraits { - static constexpr int NLayers{7}; - using TimeFrame7 = TimeFrame; + using IndexTableUtilsN = IndexTableUtils; + using TimeFrameN = TimeFrame; public: VertexerTraits() = default; @@ -61,8 +58,8 @@ class VertexerTraits return int4{0, 0, 0, 0}; } GPUhd() const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); - 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 @@ -70,78 +67,77 @@ class VertexerTraits virtual void computeTracklets(const int iteration = 0); virtual void computeTrackletMatching(const int iteration = 0); virtual void computeVertices(const int iteration = 0); - virtual void adoptTimeFrame(TimeFrame7* tf) noexcept { mTimeFrame = tf; } + virtual void adoptTimeFrame(TimeFrameN* tf) noexcept { mTimeFrame = tf; } virtual void updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& gpuTfPar); - void computeVerticesInRof(int, - gsl::span&, - bounded_vector&, - bounded_vector&, - std::array&, - bounded_vector&, - bounded_vector&, - TimeFrame7*, - bounded_vector*, - const int iteration = 0); - - bounded_vector> selectClusters(const int* indexTable, - const std::array& selectedBinsRect, - const IndexTableUtils& utils); + // truth tracking + void addTruthSeedingVertices(); // utils auto& getVertexingParameters() { return mVrtParams; } auto getVertexingParameters() const { return mVrtParams; } void setVertexingParameters(std::vector& vertParams) { mVrtParams = vertParams; } - void dumpVertexerTraits(); - void setNThreads(int n); - int getNThreads() const { return mNThreads; } + 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; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - template - static std::pair computeMain(const bounded_vector& elements) + static std::pair computeMain(const bounded_vector& elements) { - T elem; + // 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 (auto& element : elements) { - size_t count = std::count(elements.begin(), elements.end(), element); + for (const auto& [key, count] : frequency) { if (count > maxCount) { maxCount = count; - elem = element; + elem = key; } } - return std::make_pair(elem, static_cast(maxCount) / elements.size()); + return std::make_pair(elem, static_cast(maxCount) / static_cast(elements.size())); } protected: - int mNThreads = 1; - std::vector mVrtParams; - IndexTableUtils mIndexTableUtils; + IndexTableUtilsN mIndexTableUtils; // Frame related quantities - TimeFrame7* mTimeFrame = nullptr; // observer ptr + TimeFrameN* mTimeFrame = nullptr; // observer ptr private: std::shared_ptr mMemoryPool; - tbb::task_arena mTaskArena; + 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, const int iteration) +template +inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) { 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; @@ -159,8 +155,9 @@ 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); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx b/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx index 78f6683675947..c4d288bd61777 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx @@ -37,7 +37,8 @@ Cluster::Cluster(const float x, const float y, const float z, const int index) // Nothing to do } -Cluster::Cluster(const int layerIndex, const IndexTableUtils& utils, const Cluster& other) +template +Cluster::Cluster(const int layerIndex, const IndexTableUtils& utils, const Cluster& other) : xCoordinate{other.xCoordinate}, yCoordinate{other.yCoordinate}, zCoordinate{other.zCoordinate}, @@ -51,7 +52,8 @@ Cluster::Cluster(const int layerIndex, const IndexTableUtils& utils, const Clust // Nothing to do } -Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const IndexTableUtils& utils, const Cluster& other) +template +Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const IndexTableUtils& utils, const Cluster& other) : xCoordinate{other.xCoordinate}, yCoordinate{other.yCoordinate}, zCoordinate{other.zCoordinate}, diff --git a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx index 570f58ca2695d..1a0fa1d3908a4 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx @@ -19,7 +19,6 @@ namespace its { Line::Line(std::array firstPoint, std::array secondPoint) - : weightMatrix{1., 0., 0., 1., 0., 1.} // dummy, ATM { for (int index{0}; index < 3; ++index) { originPoint[index] = firstPoint.data()[index]; @@ -95,9 +94,9 @@ ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const in std::array covarianceFirst{1., 1., 1.}; std::array covarianceSecond{1., 1., 1.}; - for (int i{0}; i < 6; ++i) { - mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; - } + // for (int i{0}; i < 6; ++i) { + // mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; + // } float determinantFirst = firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + @@ -193,9 +192,9 @@ ClusterLines::ClusterLines(const Line& firstLine, const Line& secondLine) std::array covarianceSecond{1., 1., 1.}; updateROFPoll(firstLine); updateROFPoll(secondLine); - for (int i{0}; i < 6; ++i) { - mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; - } + // for (int i{0}; i < 6; ++i) { + // mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; + // } float determinantFirst = firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + @@ -281,9 +280,9 @@ void ClusterLines::add(const int& lineLabel, const Line& line, const bool& weigh updateROFPoll(line); std::array covariance{1., 1., 1.}; - for (int i{0}; i < 6; ++i) { - mWeightMatrix[i] += line.weightMatrix[i]; - } + // for (int i{0}; i < 6; ++i) { + // mWeightMatrix[i] += line.weightMatrix[i]; + // } // if(weight) line->GetSigma2P0(covariance); double determinant{line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[0] * covariance[1] + @@ -370,25 +369,25 @@ bool ClusterLines::operator==(const ClusterLines& rhs) const GPUhdi() void ClusterLines::updateROFPoll(const Line& line) { // option 1: Boyer-Moore voting for rof label - // if (mROFWeight == 0) { - // mROF = line.getMinROF(); - // mROFWeight = 1; - // } else { - // if (mROF == line.getMinROF()) { - // mROFWeight++; - // } else { - // mROFWeight--; - // } - // } - - // option 2 - if (mROF == -1) { + if (mROFWeight == 0) { mROF = line.getMinROF(); + mROFWeight = 1; } else { - if (line.getMinROF() < mROF) { - mROF = line.getMinROF(); + if (mROF == line.getMinROF()) { + mROFWeight++; + } else { + mROFWeight--; } } + + // option 2 + // if (mROF == -1) { + // mROF = line.getMinROF(); + // } else { + // if (line.getMinROF() < mROF) { + // mROF = line.getMinROF(); + // } + // } } } // namespace its diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 9e631ad6afb7f..202dc87f04237 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -9,39 +9,23 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include #include -#include "ITStracking/Constants.h" +#include +#include +#include +#include "Framework/Logger.h" +#include "ITStracking/Constants.h" #include "ITStracking/Configuration.h" +#include "ITStracking/TrackingConfigParam.h" -namespace o2::its -{ - -std::string asString(TrackingMode mode) -{ - switch (mode) { - case TrackingMode::Sync: - return "sync"; - case TrackingMode::Async: - return "async"; - case TrackingMode::Cosmics: - return "cosmics"; - case TrackingMode::Unset: - return "unset"; - } - return "unknown"; -} - -std::ostream& operator<<(std::ostream& os, TrackingMode v) -{ - os << asString(v); - return os; -} +using namespace o2::its; std::string TrackingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} NROFIt:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", - ZBins, PhiBins, nROFsPerIterations, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); + std::string str = std::format("NZb:{} NPhB:{} NROFIt:{} DRof:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", + ZBins, PhiBins, nROFsPerIterations, DeltaROF, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); bool first = true; for (int il = NLayers; il >= MinTrackLength; il--) { int slot = NLayers - il; @@ -72,4 +56,256 @@ std::string VertexingParameters::asString() const return str; } -} // namespace o2::its +namespace +{ +constexpr bool iequals(std::string_view a, std::string_view b) +{ + return std::equal(a.begin(), a.end(), b.begin(), b.end(), + [](char x, char y) { return std::tolower(x) == std::tolower(y); }); +} +} // namespace + +TrackingMode::Type TrackingMode::fromString(std::string_view str) +{ + constexpr std::array smodes = { + std::pair{"sync", Sync}, + std::pair{"async", Async}, + std::pair{"cosmics", Cosmics}, + std::pair{"unset", Unset}, + std::pair{"off", Off}}; + + auto it = std::find_if(smodes.begin(), smodes.end(), [&str](const auto& pair) { + return iequals(str, pair.first); + }); + if (it == smodes.end()) { + LOGP(fatal, "Unrecognized tracking mode '{}'", str); + } + return it->second; +} + +std::string TrackingMode::toString(TrackingMode::Type mode) +{ + if (mode == TrackingMode::Sync) { + return "sync"; + } else if (mode == TrackingMode::Async) { + return "async"; + } else if (mode == TrackingMode::Cosmics) { + return "cosmics"; + } else if (mode == TrackingMode::Unset) { + return "unset"; + } else if (mode == TrackingMode::Off) { + return "off"; + } + LOGP(fatal, "Unrecognized tracking mode '{}'", (int)mode); + return ""; // not reachable +} + +std::vector TrackingMode::getTrackingParameters(TrackingMode::Type mode) +{ + const auto& tc = o2::its::TrackerParamConfig::Instance(); + std::vector trackParams; + + if (mode == TrackingMode::Async) { + trackParams.resize(tc.doUPCIteration ? 4 : 3); + trackParams[1].TrackletMinPt = 0.2f; + trackParams[1].CellDeltaTanLambdaSigma *= 2.; + trackParams[2].TrackletMinPt = 0.1f; + trackParams[2].CellDeltaTanLambdaSigma *= 4.; + + trackParams[0].MinPt[0] = 1.f / 12; // 7cl + trackParams[1].MinPt[0] = 1.f / 12; // 7cl + + trackParams[2].MinTrackLength = 4; + trackParams[2].MinPt[0] = 1.f / 12; // 7cl + trackParams[2].MinPt[1] = 1.f / 5; // 6cl + trackParams[2].MinPt[2] = 1.f / 1; // 5cl + trackParams[2].MinPt[3] = 1.f / 6; // 4cl + + trackParams[2].StartLayerMask = (1 << 6) + (1 << 3); + if (tc.doUPCIteration) { + trackParams[3].MinTrackLength = 4; + trackParams[3].TrackletMinPt = 0.1f; + trackParams[3].CellDeltaTanLambdaSigma *= 4.; + trackParams[3].DeltaROF = 0; // UPC specific setting + } + for (size_t ip = 0; ip < trackParams.size(); ip++) { + auto& param = trackParams[ip]; + param.ZBins = 64; + param.PhiBins = 32; + param.CellsPerClusterLimit = 1.e3f; + param.TrackletsPerClusterLimit = 1.e3f; + // check if something was overridden via configurable params + if (ip < tc.MaxIter) { + if (tc.startLayerMask[ip] > 0) { + trackParams[2].StartLayerMask = tc.startLayerMask[ip]; + } + if (tc.minTrackLgtIter[ip] > 0) { + param.MinTrackLength = tc.minTrackLgtIter[ip]; + } + for (int ilg = tc.MaxTrackLength; ilg >= tc.MinTrackLength; ilg--) { + int lslot0 = (tc.MaxTrackLength - ilg), lslot = lslot0 + ip * (tc.MaxTrackLength - tc.MinTrackLength + 1); + if (tc.minPtIterLgt[lslot] > 0.) { + param.MinPt[lslot0] = tc.minPtIterLgt[lslot]; + } + } + } + } + } else if (mode == TrackingMode::Sync) { + trackParams.resize(1); + trackParams[0].ZBins = 64; + trackParams[0].PhiBins = 32; + trackParams[0].MinTrackLength = 4; + } else if (mode == TrackingMode::Cosmics) { + trackParams.resize(1); + trackParams[0].MinTrackLength = 4; + trackParams[0].CellDeltaTanLambdaSigma *= 10; + trackParams[0].PhiBins = 4; + trackParams[0].ZBins = 16; + trackParams[0].PVres = 1.e5f; + trackParams[0].MaxChi2ClusterAttachment = 60.; + trackParams[0].MaxChi2NDF = 40.; + trackParams[0].TrackletsPerClusterLimit = 100.; + trackParams[0].CellsPerClusterLimit = 100.; + } else { + LOGP(fatal, "Unsupported ITS tracking mode {} ", toString(mode)); + } + + float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791; + float bFactorTracklets = bFactor < 0.01 ? 1. : bFactor; // for tracklets only + int nROFsPerIterations = tc.nROFsPerIterations > 0 ? tc.nROFsPerIterations : -1; + + if (tc.nOrbitsPerIterations > 0) { + /// code to be used when the number of ROFs per orbit is known, this gets priority over the number of ROFs per iteration + } + + // global parameters set for every iteration + for (auto& p : trackParams) { + // adjust pT settings to actual mag. field + p.TrackletMinPt *= bFactorTracklets; + for (int ilg = tc.MaxTrackLength; ilg >= tc.MinTrackLength; ilg--) { + int lslot = tc.MaxTrackLength - ilg; + p.MinPt[lslot] *= bFactor; + } + p.ReseedIfShorter = tc.reseedIfShorter; + p.RepeatRefitOut = tc.repeatRefitOut; + p.ShiftRefToCluster = tc.shiftRefToCluster; + p.createArtefactLabels = tc.createArtefactLabels; + + p.PrintMemory = tc.printMemory; + p.MaxMemory = tc.maxMemory; + p.DropTFUponFailure = tc.dropTFUponFailure; + p.SaveTimeBenchmarks = tc.saveTimeBenchmarks; + p.FataliseUponFailure = tc.fataliseUponFailure; + p.AllowSharingFirstCluster = tc.allowSharingFirstCluster; + + if (tc.useMatCorrTGeo) { + p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrTGeo; + } else if (tc.useFastMaterial) { + p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; + } else { + p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; + } + + if (p.NLayers == 7) { + for (int i{0}; i < 7; ++i) { + p.SystErrorY2[i] = tc.sysErrY2[i] > 0 ? tc.sysErrY2[i] : p.SystErrorY2[i]; + p.SystErrorZ2[i] = tc.sysErrZ2[i] > 0 ? tc.sysErrZ2[i] : p.SystErrorZ2[i]; + } + } + p.DeltaROF = tc.deltaRof; + p.DoUPCIteration = tc.doUPCIteration; + p.MaxChi2ClusterAttachment = tc.maxChi2ClusterAttachment > 0 ? tc.maxChi2ClusterAttachment : p.MaxChi2ClusterAttachment; + p.MaxChi2NDF = tc.maxChi2NDF > 0 ? tc.maxChi2NDF : p.MaxChi2NDF; + p.PhiBins = tc.LUTbinsPhi > 0 ? tc.LUTbinsPhi : p.PhiBins; + p.ZBins = tc.LUTbinsZ > 0 ? tc.LUTbinsZ : p.ZBins; + p.PVres = tc.pvRes > 0 ? tc.pvRes : p.PVres; + p.NSigmaCut *= tc.nSigmaCut > 0 ? tc.nSigmaCut : 1.f; + p.CellDeltaTanLambdaSigma *= tc.deltaTanLres > 0 ? tc.deltaTanLres : 1.f; + p.TrackletMinPt *= tc.minPt > 0 ? tc.minPt : 1.f; + p.nROFsPerIterations = nROFsPerIterations; + p.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; + for (int iD{0}; iD < 3; ++iD) { + p.Diamond[iD] = tc.diamondPos[iD]; + } + p.UseDiamond = tc.useDiamond; + if (tc.useTrackFollower > 0) { + p.UseTrackFollower = true; + // Bit 0: Allow for mixing of top&bot extension --> implies Bits 1&2 set + // Bit 1: Allow for top extension + // Bit 2: Allow for bot extension + p.UseTrackFollowerMix = ((tc.useTrackFollower & (1 << 0)) != 0); + p.UseTrackFollowerTop = ((tc.useTrackFollower & (1 << 1)) != 0); + p.UseTrackFollowerBot = ((tc.useTrackFollower & (1 << 2)) != 0); + p.TrackFollowerNSigmaCutZ = tc.trackFollowerNSigmaZ; + p.TrackFollowerNSigmaCutPhi = tc.trackFollowerNSigmaPhi; + } + if (tc.cellsPerClusterLimit >= 0) { + p.CellsPerClusterLimit = tc.cellsPerClusterLimit; + } + if (tc.trackletsPerClusterLimit >= 0) { + p.TrackletsPerClusterLimit = tc.trackletsPerClusterLimit; + } + if (tc.findShortTracks >= 0) { + p.FindShortTracks = tc.findShortTracks; + } + } + + if (trackParams.size() > tc.nIterations) { + trackParams.resize(tc.nIterations); + } + + return trackParams; +} + +std::vector TrackingMode::getVertexingParameters(TrackingMode::Type mode) +{ + const auto& vc = o2::its::VertexerParamConfig::Instance(); + std::vector vertParams; + if (mode == TrackingMode::Async) { + vertParams.resize(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice + vertParams[1].phiCut = 0.015f; + vertParams[1].tanLambdaCut = 0.015f; + vertParams[1].vertPerRofThreshold = 0; + vertParams[1].deltaRof = 0; + } else if (mode == TrackingMode::Sync) { + vertParams.resize(1); + } else if (mode == TrackingMode::Cosmics) { + vertParams.resize(1); + } else { + LOGP(fatal, "Unsupported ITS vertexing mode {} ", toString(mode)); + } + + // global parameters set for every iteration + for (auto& p : vertParams) { + p.SaveTimeBenchmarks = vc.saveTimeBenchmarks; + p.PrintMemory = vc.printMemory; + p.MaxMemory = vc.maxMemory; + p.DropTFUponFailure = vc.dropTFUponFailure; + p.nIterations = vc.nIterations; + p.deltaRof = vc.deltaRof; + p.allowSingleContribClusters = vc.allowSingleContribClusters; + p.trackletSigma = vc.trackletSigma; + p.maxZPositionAllowed = vc.maxZPositionAllowed; + p.clusterContributorsCut = vc.clusterContributorsCut; + p.phiSpan = vc.phiSpan; + p.nThreads = vc.nThreads; + p.ZBins = vc.ZBins; + p.PhiBins = vc.PhiBins; + + p.useTruthSeeding = vc.useTruthSeeding; + p.outputContLabels = vc.outputContLabels; + } + // set for now outside to not disturb status quo + vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; + vertParams[0].vertRadiusSigma = vc.vertRadiusSigma; + vertParams[0].maxTrackletsPerCluster = vc.maxTrackletsPerCluster; + vertParams[0].lowMultBeamDistCut = vc.lowMultBeamDistCut; + vertParams[0].zCut = vc.zCut; + vertParams[0].phiCut = vc.phiCut; + vertParams[0].pairCut = vc.pairCut; + vertParams[0].clusterCut = vc.clusterCut; + vertParams[0].histPairCut = vc.histPairCut; + vertParams[0].tanLambdaCut = vc.tanLambdaCut; + + return vertParams; +} diff --git a/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx index 2638b437d61f8..e2ce374ed1600 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx @@ -17,20 +17,11 @@ #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "ITSBase/GeometryTGeo.h" -#include "ITStracking/Constants.h" -#include "MathUtils/Utils.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "ITStracking/TrackingConfigParam.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" namespace { @@ -46,10 +37,10 @@ void ioutils::convertCompactClusters(gsl::span clu std::vector>& output, const itsmft::TopologyDictionary* dict) { + static const o2::itsmft::ChipMappingITS chmap; GeometryTGeo* geom = GeometryTGeo::Instance(); bool applyMisalignment = false; const auto& conf = TrackerParamConfig::Instance(); - const auto& chmap = getChipMappingITS(); for (int il = 0; il < chmap.NLayers; il++) { if (conf.sysErrY2[il] > 0.f || conf.sysErrZ2[il] > 0.f) { applyMisalignment = true; @@ -69,77 +60,3 @@ void ioutils::convertCompactClusters(gsl::span clu cl3d.setErrors(sigmaY2, sigmaZ2, sigmaYZ); } } - -void ioutils::loadEventData(ROframe& event, gsl::span clusters, - gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* clsLabels) -{ - if (clusters.empty()) { - std::cerr << "Missing clusters." << std::endl; - return; - } - event.clear(); - GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - int clusterId{0}; - - for (const auto& c : clusters) { - const int layer = geom->getLayer(c.getSensorID()); - float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; - auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2); - auto sensorID = c.getSensorID(); - // Inverse transformation to the local --> tracking - auto trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; - // Transformation to the local --> global - auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - - event.addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), - std::array{trkXYZ.y(), trkXYZ.z()}, - std::array{sigmaY2, sigmaYZ, sigmaZ2}); - - /// Rotate to the global frame - event.addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), event.getClustersOnLayer(layer).size()); - if (clsLabels) { - // event.addClusterLabelToLayer(layer, *(clsLabels->getLabels(clusterId).begin())); - event.setMClabelsContainer(clsLabels); - } - event.addClusterExternalIndexToLayer(layer, clusterId); - clusterId++; - } -} - -int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, gsl::span clusters, gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mcLabels) -{ - event.clear(); - GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - int clusterId{0}; - - auto first = rof.getFirstEntry(); - auto clusters_in_frame = rof.getROFData(clusters); - for (const auto& c : clusters_in_frame) { - const int layer = geom->getLayer(c.getSensorID()); - float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; - auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2); - auto sensorID = c.getSensorID(); - // Inverse transformation to the local --> tracking - auto trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; - // Transformation to the local --> global - auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - - event.addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), - std::array{trkXYZ.y(), trkXYZ.z()}, - std::array{sigmaY2, sigmaYZ, sigmaZ2}); - - /// Rotate to the global frame - event.addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), event.getClustersOnLayer(layer).size()); - if (mcLabels) { - // event.addClusterLabelToLayer(layer, *(mcLabels->getLabels(first + clusterId).begin())); - event.setMClabelsContainer(mcLabels); - } - event.addClusterExternalIndexToLayer(layer, first + clusterId); - clusterId++; - } - return (int)clusters_in_frame.size(); -} diff --git a/Detectors/ITSMFT/ITS/tracking/src/Label.cxx b/Detectors/ITSMFT/ITS/tracking/src/Label.cxx deleted file mode 100644 index e195318828f51..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/Label.cxx +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Label.cxx -/// \brief -/// - -#include "ITStracking/Label.h" - -namespace o2 -{ -namespace its -{ - -Label::Label(const int mcId, const float pT, const float phi, const float eta, const int pdg, const int ncl) - : monteCarloId{mcId}, - transverseMomentum{pT}, - phi{phi}, - pseudorapidity{eta}, - pdgCode{pdg}, - numberOfClusters{ncl} -{ - // Nothing to do -} - -std::ostream& operator<<(std::ostream& outputStream, const Label& label) -{ - outputStream << label.monteCarloId << "\t" << label.transverseMomentum << "\t" << label.phi << "\t" - << label.pseudorapidity << "\t" << label.pdgCode << "\t" << label.numberOfClusters; - - return outputStream; -} -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx b/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx deleted file mode 100644 index ee885db3c49ea..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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.cxx -/// \brief -/// - -#include "ITStracking/ROframe.h" - -#include - -namespace o2 -{ -namespace its -{ - -ROframe::ROframe(int ROframeId, int nLayers) : mROframeId{ROframeId} -{ - mClusters.resize(nLayers); - mTrackingFrameInfo.resize(nLayers); - // mClusterLabels.resize(nLayers); - mClusterExternalIndices.resize(nLayers); -} - -void ROframe::addPrimaryVertex(const float xCoordinate, const float yCoordinate, const float zCoordinate) -{ - mPrimaryVertices.emplace_back(float3{xCoordinate, yCoordinate, zCoordinate}); -} - -void ROframe::addPrimaryVertices(std::vector vertices) -{ - for (Vertex& vertex : vertices) { - mPrimaryVertices.emplace_back(float3{vertex.getX(), vertex.getY(), vertex.getZ()}); - } -} - -void ROframe::printPrimaryVertices() const -{ - const int verticesNum{static_cast(mPrimaryVertices.size())}; - - for (int iVertex{0}; iVertex < verticesNum; ++iVertex) { - - const float3& currentVertex = mPrimaryVertices[iVertex]; - std::cout << "-1\t" << currentVertex.x << "\t" << currentVertex.y << "\t" << currentVertex.z << std::endl; - } -} - -int ROframe::getTotalClusters() const -{ - size_t totalClusters{0}; - for (auto& clusters : mClusters) { - totalClusters += clusters.size(); - } - return int(totalClusters); -} -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx b/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx index 9bc65161c3cbb..f2f7dbc81398f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx @@ -25,92 +25,92 @@ constexpr std::array getInverseSymm2D(const std::array& ma } // Smoother -template -Smoother::Smoother(TrackITSExt& track, size_t smoothingLayer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr) : mLayerToSmooth{smoothingLayer}, - mBz(bZ), - mCorr(corr) -{ - - auto propInstance = o2::base::Propagator::Instance(); - const TrackingFrameInfo& originalTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(track.getClusterIndex(mLayerToSmooth)); - - mOutwardsTrack = track; // This track will be propagated outwards inside the smoother! (as last step of fitting did inward propagation) - mInwardsTrack = {track.getParamOut(), // This track will be propagated inwards inside the smoother! - static_cast(mOutwardsTrack.getNumberOfClusters()), -999, static_cast(event.getROFrameId()), - mOutwardsTrack.getParamOut(), mOutwardsTrack.getClusterIndexes()}; - - mOutwardsTrack.resetCovariance(); - mOutwardsTrack.setChi2(0); - mInwardsTrack.resetCovariance(); - mInwardsTrack.setChi2(0); - - bool statusOutw{false}; - bool statusInw{false}; - - ////////////////////// - // Outward propagation - for (size_t iLayer{0}; iLayer < mLayerToSmooth; ++iLayer) { - if (mOutwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks - continue; - } - const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mOutwardsTrack.getClusterIndex(iLayer)); - statusOutw = mOutwardsTrack.rotate(tF.alphaTrackingFrame); - statusOutw &= propInstance->propagateToX(mOutwardsTrack, - tF.xTrackingFrame, - mBz, - o2::base::PropagatorImpl::MAX_SIN_PHI, - o2::base::PropagatorImpl::MAX_STEP, - mCorr); - mOutwardsTrack.setChi2(mOutwardsTrack.getChi2() + mOutwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); - statusOutw &= mOutwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); - // LOG(info) << "Outwards loop on inwards track, layer: " << iLayer << " x: " << mOutwardsTrack.getX(); - } - - // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible - auto outwardsClone = mOutwardsTrack; - statusOutw = outwardsClone.rotate(originalTf.alphaTrackingFrame); - statusOutw &= propInstance->propagateToX(outwardsClone, - originalTf.xTrackingFrame, - mBz, - o2::base::PropagatorImpl::MAX_SIN_PHI, - o2::base::PropagatorImpl::MAX_STEP, - mCorr); - ///////////////////// - // Inward propagation - for (size_t iLayer{D - 1}; iLayer > mLayerToSmooth; --iLayer) { - if (mInwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks - continue; - } - const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mInwardsTrack.getClusterIndex(iLayer)); - statusInw = mInwardsTrack.rotate(tF.alphaTrackingFrame); - statusInw &= propInstance->propagateToX(mInwardsTrack, - tF.xTrackingFrame, - mBz, - o2::base::PropagatorImpl::MAX_SIN_PHI, - o2::base::PropagatorImpl::MAX_STEP, - mCorr); - mInwardsTrack.setChi2(mInwardsTrack.getChi2() + mInwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); - statusInw &= mInwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); - // LOG(info) << "Inwards loop on outwards track, layer: " << iLayer << " x: " << mInwardsTrack.getX(); - } - - // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not revesible - auto inwardsClone = mInwardsTrack; - statusInw = inwardsClone.rotate(originalTf.alphaTrackingFrame); - statusInw &= propInstance->propagateToX(inwardsClone, - originalTf.xTrackingFrame, - mBz, - o2::base::PropagatorImpl::MAX_SIN_PHI, - o2::base::PropagatorImpl::MAX_STEP, - mCorr); - // Compute weighted local chi2 - mInitStatus = statusInw && statusOutw; - if (mInitStatus) { - mBestChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, originalTf.positionTrackingFrame, originalTf.covarianceTrackingFrame); - mLastChi2 = mBestChi2; - LOG(info) << "Smoothed chi2 on original cluster: " << mBestChi2; - } -} +// template +// Smoother::Smoother(TrackITSExt& track, size_t smoothingLayer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr) : mLayerToSmooth{smoothingLayer}, +// mBz(bZ), +// mCorr(corr) +// { +// +// auto propInstance = o2::base::Propagator::Instance(); +// const TrackingFrameInfo& originalTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(track.getClusterIndex(mLayerToSmooth)); +// +// mOutwardsTrack = track; // This track will be propagated outwards inside the smoother! (as last step of fitting did inward propagation) +// mInwardsTrack = {track.getParamOut(), // This track will be propagated inwards inside the smoother! +// static_cast(mOutwardsTrack.getNumberOfClusters()), -999, static_cast(event.getROFrameId()), +// mOutwardsTrack.getParamOut(), mOutwardsTrack.getClusterIndexes()}; +// +// mOutwardsTrack.resetCovariance(); +// mOutwardsTrack.setChi2(0); +// mInwardsTrack.resetCovariance(); +// mInwardsTrack.setChi2(0); +// +// bool statusOutw{false}; +// bool statusInw{false}; +// +// ////////////////////// +// // Outward propagation +// for (size_t iLayer{0}; iLayer < mLayerToSmooth; ++iLayer) { +// if (mOutwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks +// continue; +// } +// const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mOutwardsTrack.getClusterIndex(iLayer)); +// statusOutw = mOutwardsTrack.rotate(tF.alphaTrackingFrame); +// statusOutw &= propInstance->propagateToX(mOutwardsTrack, +// tF.xTrackingFrame, +// mBz, +// o2::base::PropagatorImpl::MAX_SIN_PHI, +// o2::base::PropagatorImpl::MAX_STEP, +// mCorr); +// mOutwardsTrack.setChi2(mOutwardsTrack.getChi2() + mOutwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); +// statusOutw &= mOutwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); +// // LOG(info) << "Outwards loop on inwards track, layer: " << iLayer << " x: " << mOutwardsTrack.getX(); +// } +// +// // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible +// auto outwardsClone = mOutwardsTrack; +// statusOutw = outwardsClone.rotate(originalTf.alphaTrackingFrame); +// statusOutw &= propInstance->propagateToX(outwardsClone, +// originalTf.xTrackingFrame, +// mBz, +// o2::base::PropagatorImpl::MAX_SIN_PHI, +// o2::base::PropagatorImpl::MAX_STEP, +// mCorr); +// ///////////////////// +// // Inward propagation +// for (size_t iLayer{D - 1}; iLayer > mLayerToSmooth; --iLayer) { +// if (mInwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks +// continue; +// } +// const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mInwardsTrack.getClusterIndex(iLayer)); +// statusInw = mInwardsTrack.rotate(tF.alphaTrackingFrame); +// statusInw &= propInstance->propagateToX(mInwardsTrack, +// tF.xTrackingFrame, +// mBz, +// o2::base::PropagatorImpl::MAX_SIN_PHI, +// o2::base::PropagatorImpl::MAX_STEP, +// mCorr); +// mInwardsTrack.setChi2(mInwardsTrack.getChi2() + mInwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); +// statusInw &= mInwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); +// // LOG(info) << "Inwards loop on outwards track, layer: " << iLayer << " x: " << mInwardsTrack.getX(); +// } +// +// // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not revesible +// auto inwardsClone = mInwardsTrack; +// statusInw = inwardsClone.rotate(originalTf.alphaTrackingFrame); +// statusInw &= propInstance->propagateToX(inwardsClone, +// originalTf.xTrackingFrame, +// mBz, +// o2::base::PropagatorImpl::MAX_SIN_PHI, +// o2::base::PropagatorImpl::MAX_STEP, +// mCorr); +// // Compute weighted local chi2 +// mInitStatus = statusInw && statusOutw; +// if (mInitStatus) { +// mBestChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, originalTf.positionTrackingFrame, originalTf.covarianceTrackingFrame); +// mLastChi2 = mBestChi2; +// LOG(info) << "Smoothed chi2 on original cluster: " << mBestChi2; +// } +// } template Smoother::~Smoother() = default; @@ -173,48 +173,48 @@ float Smoother::computeSmoothedPredictedChi2(const o2::track::TrackParCov& fi return chi2; } -template -bool Smoother::testCluster(const int clusterId, const ROframe& event) -{ - if (!mInitStatus) { - return false; - } - auto propInstance = o2::base::Propagator::Instance(); - const TrackingFrameInfo& testTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(clusterId); - - bool statusOutw{false}; - bool statusInw{false}; - - // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible - auto outwardsClone = mOutwardsTrack; - statusOutw = outwardsClone.rotate(testTf.alphaTrackingFrame); - statusOutw &= propInstance->propagateToX(outwardsClone, - testTf.xTrackingFrame, - mBz, - o2::base::PropagatorImpl::MAX_SIN_PHI, - o2::base::PropagatorImpl::MAX_STEP, - mCorr); - - // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not reversible - auto inwardsClone = mInwardsTrack; - statusInw = inwardsClone.rotate(testTf.alphaTrackingFrame); - statusInw &= propInstance->propagateToX(inwardsClone, - testTf.xTrackingFrame, - mBz, - o2::base::PropagatorImpl::MAX_SIN_PHI, - o2::base::PropagatorImpl::MAX_STEP, - mCorr); - if (!(statusOutw && statusInw)) { - LOG(warning) << "Failed propagation in smoother!"; - return false; - } - - // Compute weighted local chi2 - mLastChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, testTf.positionTrackingFrame, testTf.covarianceTrackingFrame); - LOG(info) << "Smoothed chi2 on tested cluster: " << mLastChi2; - - return true; -} +// template +// bool Smoother::testCluster(const int clusterId, const ROframe& event) +// { +// if (!mInitStatus) { +// return false; +// } +// auto propInstance = o2::base::Propagator::Instance(); +// const TrackingFrameInfo& testTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(clusterId); +// +// bool statusOutw{false}; +// bool statusInw{false}; +// +// // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible +// auto outwardsClone = mOutwardsTrack; +// statusOutw = outwardsClone.rotate(testTf.alphaTrackingFrame); +// statusOutw &= propInstance->propagateToX(outwardsClone, +// testTf.xTrackingFrame, +// mBz, +// o2::base::PropagatorImpl::MAX_SIN_PHI, +// o2::base::PropagatorImpl::MAX_STEP, +// mCorr); +// +// // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not reversible +// auto inwardsClone = mInwardsTrack; +// statusInw = inwardsClone.rotate(testTf.alphaTrackingFrame); +// statusInw &= propInstance->propagateToX(inwardsClone, +// testTf.xTrackingFrame, +// mBz, +// o2::base::PropagatorImpl::MAX_SIN_PHI, +// o2::base::PropagatorImpl::MAX_STEP, +// mCorr); +// if (!(statusOutw && statusInw)) { +// LOG(warning) << "Failed propagation in smoother!"; +// return false; +// } +// +// // Compute weighted local chi2 +// mLastChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, testTf.positionTrackingFrame, testTf.covarianceTrackingFrame); +// LOG(info) << "Smoothed chi2 on tested cluster: " << mLastChi2; +// +// return true; +// } template class Smoother<7>; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 1a3132413c450..29fb4ac4c69b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -47,42 +47,31 @@ constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; template -TimeFrame::TimeFrame() -{ - resetVectors(); -} - -template -TimeFrame::~TimeFrame() -{ - wipe(); -} - -template -void TimeFrame::addPrimaryVertices(const bounded_vector& vertices) +void TimeFrame::addPrimaryVertices(const bounded_vector& vertices, const int iteration) { for (const auto& vertex : vertices) { - mPrimaryVertices.emplace_back(vertex); - if (!isBeamPositionOverridden) { + mPrimaryVertices.emplace_back(vertex); // put a copy in the present + mTotVertPerIteration[iteration]++; + if (!isBeamPositionOverridden) { // beam position is updated only at first occurrence of the vertex. A bit sketchy if we have past/future vertices, it should not impact too much. const float w = vertex.getNContributors(); mBeamPos[0] = (mBeamPos[0] * mBeamPosWeight + vertex.getX() * w) / (mBeamPosWeight + w); mBeamPos[1] = (mBeamPos[1] * mBeamPosWeight + vertex.getY() * w) / (mBeamPosWeight + w); mBeamPosWeight += w; } } - mROFramesPV.push_back(mPrimaryVertices.size()); + mROFramesPV.push_back(mPrimaryVertices.size()); // current rof must have number of vertices up to present } template -void TimeFrame::addPrimaryVertices(const bounded_vector& vertices, const int rofId, const int iteration) +void TimeFrame::addPrimaryVerticesLabels(bounded_vector>& labels) { - addPrimaryVertices(gsl::span(vertices), rofId, iteration); + mVerticesMCRecInfo.insert(mVerticesMCRecInfo.end(), labels.begin(), labels.end()); } template -void TimeFrame::addPrimaryVerticesLabels(bounded_vector>& labels) +void TimeFrame::addPrimaryVerticesContributorLabels(bounded_vector& labels) { - mVerticesMCRecInfo.insert(mVerticesMCRecInfo.end(), labels.begin(), labels.end()); + mVerticesContributorLabels.insert(mVerticesContributorLabels.end(), labels.begin(), labels.end()); } template @@ -102,59 +91,32 @@ void TimeFrame::addPrimaryVerticesLabelsInROF(const bounded_vector -void TimeFrame::addPrimaryVertices(const gsl::span& vertices, const int rofId, const int iteration) +void TimeFrame::addPrimaryVerticesContributorLabelsInROF(const bounded_vector& labels, const int rofId) { - bounded_vector futureVertices(mMemoryPool.get()); - for (const auto& vertex : vertices) { - if (vertex.getTimeStamp().getTimeStamp() < rofId) { // put a copy in the past - insertPastVertex(vertex, iteration); - } else { - if (vertex.getTimeStamp().getTimeStamp() > rofId) { // or put a copy in the future - futureVertices.emplace_back(vertex); - } - } - mPrimaryVertices.emplace_back(vertex); // put a copy in the present - mTotVertPerIteration[iteration]++; - if (!isBeamPositionOverridden) { // beam position is updated only at first occurrence of the vertex. A bit sketchy if we have past/future vertices, it should not impact too much. - const float w = vertex.getNContributors(); - mBeamPos[0] = (mBeamPos[0] * mBeamPosWeight + vertex.getX() * w) / (mBeamPosWeight + w); - mBeamPos[1] = (mBeamPos[1] * mBeamPosWeight + vertex.getY() * w) / (mBeamPosWeight + w); - mBeamPosWeight += w; - } - } - mROFramesPV.push_back(mPrimaryVertices.size()); // current rof must have number of vertices up to present - for (auto& vertex : futureVertices) { - mPrimaryVertices.emplace_back(vertex); - mTotVertPerIteration[iteration]++; + // count the number of cont. in rofs before and including the target rof + unsigned int n{0}; + const auto& pvs = getPrimaryVertices(0, rofId); + for (const auto& pv : pvs) { + n += pv.getNContributors(); } + mVerticesContributorLabels.insert(mVerticesContributorLabels.begin() + n, labels.begin(), labels.end()); } template -int TimeFrame::loadROFrameData(gsl::span rofs, +int TimeFrame::loadROFrameData(gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels) { - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - deepVectorClear(mUnsortedClusters[iLayer], mMemoryPool.get()); - deepVectorClear(mTrackingFrameInfo[iLayer], mMemoryPool.get()); - deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); - clearResizeBoundedVector(mROFramesClusters[iLayer], 1, mMemoryPool.get(), 0); - - if (iLayer < 2) { - deepVectorClear(mTrackletsIndexROF[iLayer], mMemoryPool.get()); - deepVectorClear(mNTrackletsPerCluster[iLayer], mMemoryPool.get()); - deepVectorClear(mNTrackletsPerClusterSum[iLayer], mMemoryPool.get()); - } - } - GeometryTGeo* geom = GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - mNrof = 0; - clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); - for (auto& rof : rofs) { + resetROFrameData(rofs.size()); + prepareROFrameData(rofs, clusters); + + for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { + const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { const auto& c = clusters[clusterId]; @@ -190,15 +152,13 @@ int TimeFrame::loadROFrameData(gsl::span rofs, addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), std::array{trkXYZ.y(), trkXYZ.z()}, std::array{sigmaY2, sigmaYZ, sigmaZ2}); - /// Rotate to the global frame addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), mUnsortedClusters[layer].size()); addClusterExternalIndexToLayer(layer, clusterId); } for (unsigned int iL{0}; iL < mUnsortedClusters.size(); ++iL) { - mROFramesClusters[iL].push_back(mUnsortedClusters[iL].size()); + mROFramesClusters[iL][iRof + 1] = mUnsortedClusters[iL].size(); // effectively calculating and exclusive sum } - mNrof++; } for (auto i = 0; i < mNTrackletsPerCluster.size(); ++i) { @@ -213,6 +173,41 @@ int TimeFrame::loadROFrameData(gsl::span rofs, return mNrof; } +template +void TimeFrame::resetROFrameData(size_t nRofs) +{ + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + deepVectorClear(mUnsortedClusters[iLayer], getMaybeFrameworkHostResource()); + deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeFrameworkHostResource()); + clearResizeBoundedVector(mROFramesClusters[iLayer], nRofs + 1, getMaybeFrameworkHostResource()); + deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); + + if (iLayer < 2) { + deepVectorClear(mTrackletsIndexROF[iLayer], mMemoryPool.get()); + deepVectorClear(mNTrackletsPerCluster[iLayer], mMemoryPool.get()); + deepVectorClear(mNTrackletsPerClusterSum[iLayer], mMemoryPool.get()); + } + } +} + +template +void TimeFrame::prepareROFrameData(gsl::span rofs, + gsl::span clusters) +{ + GeometryTGeo* geom = GeometryTGeo::Instance(); + mNrof = rofs.size(); + clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); + std::array clusterCountPerLayer{}; + for (const auto& clus : clusters) { + ++clusterCountPerLayer[geom->getLayer(clus.getSensorID())]; + } + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } +} + template void TimeFrame::prepareClusters(const TrackingParameters& trkParam, const int maxLayers) { @@ -289,6 +284,7 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter deepVectorClear(mLinesLabels); if (resetVertices) { deepVectorClear(mVerticesMCRecInfo); + deepVectorClear(mVerticesContributorLabels); } clearResizeBoundedVector(mTracks, mNrof, mMemoryPool.get()); clearResizeBoundedVector(mTracksLabel, mNrof, mMemoryPool.get()); @@ -306,11 +302,11 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter clearResizeBoundedVector(mBogusClusters, trkParam.NLayers, mMemoryPool.get()); deepVectorClear(mTrackletClusters); for (unsigned int iLayer{0}; iLayer < std::min((int)mClusters.size(), maxLayers); ++iLayer) { - clearResizeBoundedVector(mClusters[iLayer], mUnsortedClusters[iLayer].size(), mMemoryPool.get()); - clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), mMemoryPool.get()); + clearResizeBoundedVector(mClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeFrameworkHostResource(maxLayers != nLayers)); + clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeFrameworkHostResource(maxLayers != nLayers)); mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt(0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); } - clearResizeBoundedArray(mIndexTables, mNrof * (trkParam.ZBins * trkParam.PhiBins + 1), mMemoryPool.get()); + clearResizeBoundedArray(mIndexTables, mNrof * (trkParam.ZBins * trkParam.PhiBins + 1), getMaybeFrameworkHostResource(maxLayers != nLayers)); clearResizeBoundedVector(mLines, mNrof, mMemoryPool.get()); clearResizeBoundedVector(mTrackletClusters, mNrof, mMemoryPool.get()); @@ -323,6 +319,8 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } } + mMinR.fill(10000.); + mMaxR.fill(-1.); } mNTrackletsPerROF.resize(2); for (auto& v : mNTrackletsPerROF) { @@ -346,7 +344,6 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter mMSangles.resize(trkParam.NLayers); mPhiCuts.resize(mClusters.size() - 1, 0.f); - float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; for (unsigned int iLayer{0}; iLayer < nLayers; ++iLayer) { mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); @@ -354,12 +351,14 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter if (iLayer < mClusters.size() - 1) { const float& r1 = trkParam.LayerRadii[iLayer]; const float& r2 = trkParam.LayerRadii[iLayer + 1]; + oneOverR = (0.5 * oneOverR >= 1.f / r2) ? 2.f / r2 - o2::constants::math::Almost0 : oneOverR; const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer]); const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer + 1]); const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r1 * oneOverR)); const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r2 * oneOverR)); float x = r2 * cosTheta1half - r1 * cosTheta2half; float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * math_utils::Sq(x * oneOverR)) * (math_utils::Sq(0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta2half + cosTheta1half) * math_utils::Sq(res1) + math_utils::Sq(0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta1half + cosTheta2half) * math_utils::Sq(res2))); + /// the expression std::asin(0.5f * x * oneOverR) is equivalent to std::aCos(0.5f * r1 * oneOverR) - std::acos(0.5 * r2 * oneOverR) mPhiCuts[iLayer] = std::min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mMSangles[iLayer] + delta, o2::constants::math::PI * 0.5f); } } @@ -370,7 +369,7 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter if (iLayer < (int)mCells.size()) { deepVectorClear(mCells[iLayer]); deepVectorClear(mTrackletsLookupTable[iLayer]); - mTrackletsLookupTable[iLayer].resize(mClusters[iLayer + 1].size(), 0); + mTrackletsLookupTable[iLayer].resize(mClusters[iLayer + 1].size() + 1, 0); deepVectorClear(mCellLabels[iLayer]); } @@ -390,7 +389,7 @@ unsigned long TimeFrame::getArtefactsMemory() const size += sizeof(Tracklet) * trkl.size(); } for (const auto& cells : mCells) { - size += sizeof(CellSeed) * cells.size(); + size += sizeof(CellSeedN) * cells.size(); } for (const auto& cellsN : mCellsNeighbours) { size += sizeof(int) * cellsN.size(); @@ -461,34 +460,6 @@ void TimeFrame::checkTrackletLUTs() } } -template -void TimeFrame::resetVectors() -{ - mMinR.fill(10000.); - mMaxR.fill(-1.); - for (int iLayers{nLayers}; iLayers--;) { - mClusters[iLayers].clear(); - mUnsortedClusters[iLayers].clear(); - mTrackingFrameInfo[iLayers].clear(); - mClusterExternalIndices[iLayers].clear(); - mUsedClusters[iLayers].clear(); - mROFramesClusters[iLayers].clear(); - mNClustersPerROF[iLayers].clear(); - } - for (int i{2}; i--;) { - mTrackletsIndexROF[i].clear(); - } -} - -template -void TimeFrame::resetTracklets() -{ - for (auto& trkl : mTracklets) { - deepVectorClear(trkl); - } - deepVectorClear(mTrackletsLookupTable); -} - template void TimeFrame::printTrackletLUTonLayer(int i) { @@ -583,75 +554,75 @@ void TimeFrame::printSliceInfo(const int startROF, const int sliceSize) LOG(info) << "Number of seeding vertices: " << getPrimaryVertices(iROF).size(); int iVertex{0}; for (auto& v : getPrimaryVertices(iROF)) { - LOG(info) << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors."; + LOG(info) << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " + << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors."; } } } template -void TimeFrame::setMemoryPool(std::shared_ptr& pool) +void TimeFrame::setMemoryPool(std::shared_ptr pool) { - wipe(); mMemoryPool = pool; - auto initVector = [&](bounded_vector& vec) { - auto alloc = vec.get_allocator().resource(); - if (alloc != mMemoryPool.get()) { - vec = bounded_vector(mMemoryPool.get()); - } + auto initVector = [&](bounded_vector& vec, bool useExternal = false) { + std::pmr::memory_resource* mr = (useExternal) ? mExtMemoryPool.get() : mMemoryPool.get(); + deepVectorClear(vec, mr); }; - auto initArrays = [&](std::array, S>& arr) { - for (size_t i{0}; i < S; ++i) { - auto alloc = arr[i].get_allocator().resource(); - if (alloc != mMemoryPool.get()) { - arr[i] = bounded_vector(mMemoryPool.get()); - } - } - }; - auto initVectors = [&](std::vector>& vec) { - for (size_t i{0}; i < vec.size(); ++i) { - auto alloc = vec[i].get_allocator().resource(); - if (alloc != mMemoryPool.get()) { - vec[i] = bounded_vector(mMemoryPool.get()); - } + + auto initContainers = [&](Container& container, bool useExternal = false) { + for (auto& v : container) { + initVector(v, useExternal); } }; + // these will only reside on the host for the cpu part initVector(mTotVertPerIteration); - initVector(mPrimaryVertices); - initVector(mROFramesPV); - initArrays(mClusters); - initArrays(mTrackingFrameInfo); - initArrays(mClusterExternalIndices); - initArrays(mROFramesClusters); - initArrays(mNTrackletsPerCluster); - initArrays(mNTrackletsPerClusterSum); - initArrays(mNClustersPerROF); - initArrays(mIndexTables); - initArrays(mUsedClusters); - initArrays(mUnsortedClusters); + initContainers(mClusterExternalIndices); + initContainers(mNTrackletsPerCluster); + initContainers(mNTrackletsPerClusterSum); + initContainers(mNClustersPerROF); initVector(mROFramesPV); initVector(mPrimaryVertices); initVector(mRoads); - initVector(mRoadLabels); initVector(mMSangles); initVector(mPhiCuts); initVector(mPositionResolution); initVector(mClusterSize); initVector(mPValphaX); initVector(mBogusClusters); - initArrays(mTrackletsIndexROF); - initVectors(mTracks); - initVectors(mTracklets); - initVectors(mCells); - initVectors(mCellsNeighbours); - initVectors(mCellsLookupTable); + initContainers(mTrackletsIndexROF); + initContainers(mTracks); + initContainers(mTracklets); + initContainers(mCells); + initContainers(mCellsNeighbours); + initContainers(mCellsLookupTable); + // MC info (we don't know if we have MC) + initVector(mVerticesContributorLabels); + initContainers(mLinesLabels); + initContainers(mTrackletLabels); + initContainers(mCellLabels); + initVector(mRoadLabels); + initContainers(mTracksLabel); + // these will use possibly an externally provided allocator + initContainers(mClusters, hasFrameworkAllocator()); + initContainers(mUsedClusters, hasFrameworkAllocator()); + initContainers(mUnsortedClusters, hasFrameworkAllocator()); + initContainers(mIndexTables, hasFrameworkAllocator()); + initContainers(mTrackingFrameInfo, hasFrameworkAllocator()); + initContainers(mROFramesClusters, hasFrameworkAllocator()); +} + +template +void TimeFrame::setFrameworkAllocator(ExternalAllocator* ext) +{ + mExternalAllocator = ext; + mExtMemoryPool = std::make_shared(mExternalAllocator); } template void TimeFrame::wipe() { - deepVectorClear(mUnsortedClusters); deepVectorClear(mTracks); deepVectorClear(mTracklets); deepVectorClear(mCells); @@ -660,21 +631,12 @@ void TimeFrame::wipe() deepVectorClear(mCellsLookupTable); deepVectorClear(mTotVertPerIteration); deepVectorClear(mPrimaryVertices); - deepVectorClear(mROFramesPV); - deepVectorClear(mClusters); - deepVectorClear(mTrackingFrameInfo); + deepVectorClear(mTrackletsLookupTable); deepVectorClear(mClusterExternalIndices); - deepVectorClear(mROFramesClusters); deepVectorClear(mNTrackletsPerCluster); deepVectorClear(mNTrackletsPerClusterSum); deepVectorClear(mNClustersPerROF); - deepVectorClear(mIndexTables); - deepVectorClear(mUsedClusters); - deepVectorClear(mUnsortedClusters); deepVectorClear(mROFramesPV); - deepVectorClear(mPrimaryVertices); - deepVectorClear(mRoads); - deepVectorClear(mRoadLabels); deepVectorClear(mMSangles); deepVectorClear(mPhiCuts); deepVectorClear(mPositionResolution); @@ -682,9 +644,33 @@ void TimeFrame::wipe() deepVectorClear(mPValphaX); deepVectorClear(mBogusClusters); deepVectorClear(mTrackletsIndexROF); - deepVectorClear(mPrimaryVertices); + deepVectorClear(mTrackletClusters); + deepVectorClear(mLines); + // if we use the external host allocator then the assumption is that we + // don't clear the memory ourself + if (!hasFrameworkAllocator()) { + deepVectorClear(mClusters); + deepVectorClear(mUsedClusters); + deepVectorClear(mUnsortedClusters); + deepVectorClear(mIndexTables); + deepVectorClear(mTrackingFrameInfo); + deepVectorClear(mROFramesClusters); + } + // only needed to clear if we have MC info + if (hasMCinformation()) { + deepVectorClear(mLinesLabels); + deepVectorClear(mVerticesContributorLabels); + deepVectorClear(mTrackletLabels); + deepVectorClear(mCellLabels); + deepVectorClear(mRoadLabels); + deepVectorClear(mTracksLabel); + } } template class TimeFrame<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class TimeFrame<11>; +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index c92d1e8505356..658a90b37613f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -19,7 +19,6 @@ #include "ITStracking/Cell.h" #include "ITStracking/Constants.h" #include "ITStracking/IndexTableUtils.h" -#include "ITStracking/Smoother.h" #include "ITStracking/Tracklet.h" #include "ITStracking/TrackerTraits.h" #include "ITStracking/TrackingConfigParam.h" @@ -35,13 +34,19 @@ namespace o2::its { using o2::its::constants::GB; -Tracker::Tracker(TrackerTraits7* traits) : mTraits(traits) +template +Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) { /// Initialise standard configuration with 1 iteration mTrkParams.resize(1); + if (traits->isGPU()) { + ITSGpuTrackingParamConfig::Instance().maybeOverride(); + ITSGpuTrackingParamConfig::Instance().printKeyValues(true, true); + } } -void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) +template +void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) { LogFunc evalLog = [](const std::string&) {}; @@ -50,7 +55,9 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) int maxNvertices{-1}; if (mTrkParams[0].PerPrimaryVertexProcessing) { for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - maxNvertices = std::max(maxNvertices, (int)mTimeFrame->getPrimaryVertices(iROF).size()); + int minRof = o2::gpu::CAMath::Max(0, iROF - mTrkParams[0].DeltaROF); + int maxRof = o2::gpu::CAMath::Min(mTimeFrame->getNrof(), iROF + mTrkParams[0].DeltaROF); + maxNvertices = std::max(maxNvertices, (int)mTimeFrame->getPrimaryVertices(minRof, maxRof).size()); } } @@ -59,10 +66,12 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) LOGP(error, "Too much memory used during {} in iteration {} in ROF span {}-{} iVtx={}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", StateNames[mCurState], iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, iVertex, (double)mTimeFrame->getArtefactsMemory() / GB, (double)mTrkParams[iteration].MaxMemory / GB); - LOGP(error, "Exception: {}", err.what()); + if (typeid(err) != typeid(std::bad_alloc)) { // only print if the exceptions is different from what is expected + LOGP(error, "Exception: {}", err.what()); + } if (mTrkParams[iteration].DropTFUponFailure) { - mTimeFrame->wipe(); mMemoryPool->print(); + mTimeFrame->wipe(); ++mNumberOfDroppedTFs; error("...Dropping Timeframe..."); } else { @@ -78,7 +87,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) } double timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; int nTracklets{0}, nCells{0}, nNeighbours{0}, nTracks{-static_cast(mTimeFrame->getNumberOfTracks())}; - int nROFsIterations = mTrkParams[iteration].nROFsPerIterations > 0 ? mTimeFrame->getNrof() / mTrkParams[iteration].nROFsPerIterations + bool(mTimeFrame->getNrof() % mTrkParams[iteration].nROFsPerIterations) : 1; + int nROFsIterations = (mTrkParams[iteration].nROFsPerIterations > 0 && !mTimeFrame->isGPU()) ? mTimeFrame->getNrof() / mTrkParams[iteration].nROFsPerIterations + bool(mTimeFrame->getNrof() % mTrkParams[iteration].nROFsPerIterations) : 1; iVertex = std::min(maxNvertices, 0); logger(std::format("==== ITS {} Tracking iteration {} summary ====", mTraits->getName(), iteration)); @@ -127,9 +136,6 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) auto nTracksA = mTimeFrame->getNumberOfTracks(); logger(std::format(" `-> found {} additional tracks", nTracksA - nTracksB)); } - if (mTrkParams[iteration].PrintMemory) { - mMemoryPool->print(); - } if constexpr (constants::DoTimeBenchmarks) { logger(std::format("=== TimeFrame {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeFrameCounter, total, mTraits->getNThreads())); } @@ -139,13 +145,13 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) } catch (const std::bad_alloc& err) { handleException(err); return; - } catch (...) { - error("Uncaught exception, all bets are off..."); - } - - if (mTrkParams[0].PrintMemory) { - mTimeFrame->printArtefactsMemory(); - mMemoryPool->print(); + } catch (const std::exception& err) { + error(std::format("Uncaught exception, all bets are off... {}", err.what())); + // clear tracks explicitly since if not fatalising on exception this may contain partial output + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + mTimeFrame->getTracks(iROF).clear(); + } + return; } if (mTimeFrame->hasMCinformation()) { @@ -154,9 +160,15 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) rectifyClusterIndices(); ++mTimeFrameCounter; mTotalTime += total; + + if (mTrkParams[0].PrintMemory) { + mTimeFrame->printArtefactsMemory(); + mMemoryPool->print(); + } } -void Tracker::computeRoadsMClabels() +template +void Tracker::computeRoadsMClabels() { /// Moore's Voting Algorithm if (!mTimeFrame->hasMCinformation()) { @@ -169,7 +181,7 @@ void Tracker::computeRoadsMClabels() for (int iRoad{0}; iRoad < roadsNum; ++iRoad) { - Road<5>& currentRoad{mTimeFrame->getRoads()[iRoad]}; + auto& currentRoad{mTimeFrame->getRoads()[iRoad]}; std::vector> occurrences; bool isFakeRoad{false}; bool isFirstRoadCell{true}; @@ -185,7 +197,7 @@ void Tracker::computeRoadsMClabels() } } - const CellSeed& currentCell{mTimeFrame->getCells()[iCell][currentCellIndex]}; + const auto& currentCell{mTimeFrame->getCells()[iCell][currentCellIndex]}; if (isFirstRoadCell) { @@ -260,7 +272,8 @@ void Tracker::computeRoadsMClabels() } } -void Tracker::computeTracksMClabels() +template +void Tracker::computeTracksMClabels() { for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { for (auto& track : mTimeFrame->getTracks(iROF)) { @@ -318,7 +331,8 @@ void Tracker::computeTracksMClabels() } } -void Tracker::rectifyClusterIndices() +template +void Tracker::rectifyClusterIndices() { for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { for (auto& track : mTimeFrame->getTracks(iROF)) { @@ -332,84 +346,25 @@ void Tracker::rectifyClusterIndices() } } -void Tracker::getGlobalConfiguration() -{ - const auto& tc = o2::its::TrackerParamConfig::Instance(); - if (tc.useMatCorrTGeo) { - mTraits->setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrTGeo); - } else if (tc.useFastMaterial) { - mTraits->setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE); - } else { - mTraits->setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); - } - setNThreads(tc.nThreads); - int nROFsPerIterations = tc.nROFsPerIterations > 0 ? tc.nROFsPerIterations : -1; - if (tc.nOrbitsPerIterations > 0) { - /// code to be used when the number of ROFs per orbit is known, this gets priority over the number of ROFs per iteration - } - for (auto& params : mTrkParams) { - if (params.NLayers == 7) { - for (int i{0}; i < 7; ++i) { - params.SystErrorY2[i] = tc.sysErrY2[i] > 0 ? tc.sysErrY2[i] : params.SystErrorY2[i]; - params.SystErrorZ2[i] = tc.sysErrZ2[i] > 0 ? tc.sysErrZ2[i] : params.SystErrorZ2[i]; - } - } - params.DeltaROF = tc.deltaRof; - params.DoUPCIteration = tc.doUPCIteration; - params.MaxChi2ClusterAttachment = tc.maxChi2ClusterAttachment > 0 ? tc.maxChi2ClusterAttachment : params.MaxChi2ClusterAttachment; - params.MaxChi2NDF = tc.maxChi2NDF > 0 ? tc.maxChi2NDF : params.MaxChi2NDF; - params.PhiBins = tc.LUTbinsPhi > 0 ? tc.LUTbinsPhi : params.PhiBins; - params.ZBins = tc.LUTbinsZ > 0 ? tc.LUTbinsZ : params.ZBins; - params.PVres = tc.pvRes > 0 ? tc.pvRes : params.PVres; - params.NSigmaCut *= tc.nSigmaCut > 0 ? tc.nSigmaCut : 1.f; - params.CellDeltaTanLambdaSigma *= tc.deltaTanLres > 0 ? tc.deltaTanLres : 1.f; - params.TrackletMinPt *= tc.minPt > 0 ? tc.minPt : 1.f; - params.nROFsPerIterations = nROFsPerIterations; - params.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; - params.SaveTimeBenchmarks = tc.saveTimeBenchmarks; - params.FataliseUponFailure = tc.fataliseUponFailure; - params.DropTFUponFailure = tc.dropTFUponFailure; - for (int iD{0}; iD < 3; ++iD) { - params.Diamond[iD] = tc.diamondPos[iD]; - } - params.UseDiamond = tc.useDiamond; - if (tc.maxMemory) { - params.MaxMemory = tc.maxMemory; - } - if (tc.useTrackFollower > 0) { - params.UseTrackFollower = true; - // Bit 0: Allow for mixing of top&bot extension --> implies Bits 1&2 set - // Bit 1: Allow for top extension - // Bit 2: Allow for bot extension - params.UseTrackFollowerMix = ((tc.useTrackFollower & (1 << 0)) != 0); - params.UseTrackFollowerTop = ((tc.useTrackFollower & (1 << 1)) != 0); - params.UseTrackFollowerBot = ((tc.useTrackFollower & (1 << 2)) != 0); - params.TrackFollowerNSigmaCutZ = tc.trackFollowerNSigmaZ; - params.TrackFollowerNSigmaCutPhi = tc.trackFollowerNSigmaPhi; - } - if (tc.cellsPerClusterLimit >= 0) { - params.CellsPerClusterLimit = tc.cellsPerClusterLimit; - } - if (tc.trackletsPerClusterLimit >= 0) { - params.TrackletsPerClusterLimit = tc.trackletsPerClusterLimit; - } - if (tc.findShortTracks >= 0) { - params.FindShortTracks = tc.findShortTracks; - } - } -} - -void Tracker::adoptTimeFrame(TimeFrame7& tf) +template +void Tracker::adoptTimeFrame(TimeFrame& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } -void Tracker::printSummary() const +template +void Tracker::printSummary() const { auto avgTF = mTotalTime * 1.e-3 / ((mTimeFrameCounter > 0) ? (double)mTimeFrameCounter : -1.0); auto avgTFwithDropped = mTotalTime * 1.e-3 / (((mTimeFrameCounter + mNumberOfDroppedTFs) > 0) ? (double)(mTimeFrameCounter + mNumberOfDroppedTFs) : -1.0); LOGP(info, "Tracker summary: Processed {} TFs (dropped {}) in TOT={:.2f} s, AVG/TF={:.2f} ({:.2f}) s", mTimeFrameCounter, mNumberOfDroppedTFs, mTotalTime * 1.e-3, avgTF, avgTFwithDropped); } +template class Tracker<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class Tracker<11>; +#endif + } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 36636069137f3..b4ac847863d51 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -17,9 +17,11 @@ #include #include #include +#include #ifdef OPTIMISATION_OUTPUT #include +#include #endif #include @@ -41,7 +43,11 @@ using o2::base::PropagatorF; namespace o2::its { -static constexpr int debugLevel{0}; +struct PassMode { + using OnePass = std::integral_constant; + using TwoPassCount = std::integral_constant; + using TwoPassInsert = std::integral_constant; +}; template void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) @@ -64,198 +70,223 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF gsl::span diamondSpan(&diamondVert, 1); int startROF{mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * mTrkParams[iteration].nROFsPerIterations : 0}; int endROF{o2::gpu::GPUCommonMath::Min(mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : mTimeFrame->getNrof(), mTimeFrame->getNrof())}; - for (int rof0{startROF}; rof0 < endROF; ++rof0) { - gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(rof0); - const int startVtx{iVertex >= 0 ? iVertex : 0}; - const int endVtx{iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; - int minRof = o2::gpu::CAMath::Max(startROF, rof0 - mTrkParams[iteration].DeltaROF); - int maxRof = o2::gpu::CAMath::Min(endROF - 1, rof0 + mTrkParams[iteration].DeltaROF); - - mTaskArena.execute([&] { - tbb::parallel_for( - tbb::blocked_range(0, mTrkParams[iteration].TrackletsPerRoad()), - [&](const tbb::blocked_range& Layers) { - for (int iLayer = Layers.begin(); iLayer < Layers.end(); ++iLayer) { - gsl::span layer0 = mTimeFrame->getClustersOnLayer(rof0, iLayer); - if (layer0.empty()) { - continue; - } - float meanDeltaR{mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]}; - const int currentLayerClustersNum{static_cast(layer0.size())}; - for (int iCluster{0}; iCluster < currentLayerClustersNum; ++iCluster) { - const Cluster& currentCluster{layer0[iCluster]}; - const int currentSortedIndex{mTimeFrame->getSortedIndex(rof0, iLayer, iCluster)}; - - if (mTimeFrame->isClusterUsed(iLayer, currentCluster.clusterId)) { - continue; - } - const float inverseR0{1.f / currentCluster.radius}; + mTaskArena->execute([&] { + auto forTracklets = [&](auto Tag, int iLayer, int pivotROF, int base, int& offset) -> int { + if (!mTimeFrame->mMultiplicityCutMask[pivotROF]) { + return 0; + } + int minROF = o2::gpu::CAMath::Max(startROF, pivotROF - mTrkParams[iteration].DeltaROF); + int maxROF = o2::gpu::CAMath::Min(endROF - 1, pivotROF + mTrkParams[iteration].DeltaROF); + gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(minROF, maxROF); + if (primaryVertices.empty()) { + return 0; + } + const int startVtx = iVertex >= 0 ? iVertex : 0; + const int endVtx = iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, int(primaryVertices.size())) : int(primaryVertices.size()); + if (endVtx <= startVtx) { + return 0; + } - for (int iV{startVtx}; iV < endVtx; ++iV) { - const auto& primaryVertex{primaryVertices[iV]}; - if (primaryVertex.isFlagSet(2) && iteration != 3) { - continue; - } - const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTrkParams[iteration].PVres) / primaryVertex.getNContributors() + math_utils::Sq(mTimeFrame->getPositionResolution(iLayer))); + int localCount = 0; + auto& tracklets = mTimeFrame->getTracklets()[iLayer]; + auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF, iLayer); + if (layer0.empty()) { + return 0; + } - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; + const float meanDeltaR = mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]; - const float zAtRmin{tanLambda * (mTimeFrame->getMinR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (mTimeFrame->getMaxR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate}; + for (int iCluster = 0; iCluster < int(layer0.size()); ++iCluster) { + const Cluster& currentCluster = layer0[iCluster]; + const int currentSortedIndex = mTimeFrame->getSortedIndex(pivotROF, iLayer, iCluster); + if (mTimeFrame->isClusterUsed(iLayer, currentCluster.clusterId)) { + continue; + } + const float inverseR0 = 1.f / currentCluster.radius; - const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// 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 * mTimeFrame->getMSangle(iLayer)))}; + for (int iV = startVtx; iV < endVtx; ++iV) { + const auto& pv = primaryVertices[iV]; + if ((pv.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !pv.isFlagSet(Vertex::Flags::UPCMode))) { + continue; + } - const int4 selectedBinsRect{getBinsRect(currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer))}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(iLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); + const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; + const float zAtRmin = tanLambda * (mTimeFrame->getMinR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate; + const float zAtRmax = tanLambda * (mTimeFrame->getMaxR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate; + const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance); + const float sigmaZ = o2::gpu::CAMath::Sqrt( + math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * mTimeFrame->getMSangle(iLayer))); - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + auto bins = getBinsRect(currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer)); + if (bins.x == 0 && bins.y == 0 && bins.z == 0 && bins.w == 0) { + continue; + } + int phiBinsNum = bins.w - bins.y + 1; + if (phiBinsNum < 0) { + phiBinsNum += mTrkParams[iteration].PhiBins; + } - if (phiBinsNum < 0) { - phiBinsNum += mTrkParams[iteration].PhiBins; + for (int targetROF{minROF}; targetROF <= maxROF; ++targetROF) { + if (!mTimeFrame->mMultiplicityCutMask[targetROF]) { + continue; + } + auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, iLayer + 1); + if (layer1.empty()) { + continue; + } + for (int iPhi = 0; iPhi < phiBinsNum; ++iPhi) { + const int iPhiBin = (bins.y + iPhi) % mTrkParams[iteration].PhiBins; + const int firstBinIdx = mTimeFrame->mIndexTableUtils.getBinIndex(bins.x, iPhiBin); + const int maxBinIdx = firstBinIdx + (bins.z - bins.x) + 1; + const int firstRow = mTimeFrame->getIndexTable(targetROF, iLayer + 1)[firstBinIdx]; + const int lastRow = mTimeFrame->getIndexTable(targetROF, iLayer + 1)[maxBinIdx]; + for (int iNext = firstRow; iNext < lastRow; ++iNext) { + if (iNext >= int(layer1.size())) { + break; } - - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - auto layer1 = mTimeFrame->getClustersOnLayer(rof1, iLayer + 1); - if (layer1.empty()) { - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % mTrkParams[iteration].PhiBins; - const int firstBinIndex{mTimeFrame->mIndexTableUtils.getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - if constexpr (debugLevel) { - if (firstBinIndex < 0 || firstBinIndex > mTimeFrame->getIndexTable(rof1, iLayer + 1).size() || - maxBinIndex < 0 || maxBinIndex > mTimeFrame->getIndexTable(rof1, iLayer + 1).size()) { - std::cout << iLayer << "\t" << iCluster << "\t" << zAtRmin << "\t" << zAtRmax << "\t" << sigmaZ * mTrkParams[iteration].NSigmaCut << "\t" << mTimeFrame->getPhiCut(iLayer) << std::endl; - std::cout << currentCluster.zCoordinate << "\t" << primaryVertex.getZ() << "\t" << currentCluster.radius << std::endl; - std::cout << mTimeFrame->getMinR(iLayer + 1) << "\t" << currentCluster.radius << "\t" << currentCluster.zCoordinate << std::endl; - std::cout << "Illegal access to IndexTable " << firstBinIndex << "\t" << maxBinIndex << "\t" << selectedBinsRect.z << "\t" << selectedBinsRect.x << std::endl; - exit(1); - } - } - const int firstRowClusterIndex = mTimeFrame->getIndexTable(rof1, iLayer + 1)[firstBinIndex]; - const int maxRowClusterIndex = mTimeFrame->getIndexTable(rof1, iLayer + 1)[maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (int)layer1.size()) { - break; - } - - const Cluster& nextCluster{layer1[iNextCluster]}; - if (mTimeFrame->isClusterUsed(iLayer + 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)}; + const Cluster& nextCluster = layer1[iNext]; + if (mTimeFrame->isClusterUsed(iLayer + 1, nextCluster.clusterId)) { + continue; + } + float deltaPhi = o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi); + float deltaZ = o2::gpu::GPUCommonMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); #ifdef OPTIMISATION_OUTPUT - MCCompLabel label; - int currentId{currentCluster.clusterId}; - int nextId{nextCluster.clusterId}; - for (auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { - for (auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, label.isValid(), (tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate) / sigmaZ, tanLambda, resolution, sigmaZ) << std::endl; + MCCompLabel label; + int currentId{currentCluster.clusterId}; + int nextId{nextCluster.clusterId}; + for (auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { + for (auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { + if (lab1 == lab2 && lab1.isValid()) { + label = lab1; + break; + } + } + if (label.isValid()) { + break; + } + } + off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, label.isValid(), (tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate) / sigmaZ, tanLambda, resolution, sigmaZ) << std::endl; #endif - if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && - (deltaPhi < mTimeFrame->getPhiCut(iLayer) || - o2::gpu::GPUCommonMath::Abs(deltaPhi - o2::constants::math::TwoPI) < mTimeFrame->getPhiCut(iLayer))) { - if (iLayer > 0) { - mTimeFrame->getTrackletsLookupTable()[iLayer - 1][currentSortedIndex]++; - } - 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)}; - mTimeFrame->getTracklets()[iLayer].emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(rof1, iLayer + 1, iNextCluster), tanL, phi, rof0, rof1); - } - } + if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && + ((deltaPhi < mTimeFrame->getPhiCut(iLayer) || o2::gpu::GPUCommonMath::Abs(deltaPhi - o2::constants::math::TwoPI) < mTimeFrame->getPhiCut(iLayer)))) { + 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); + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, pivotROF, targetROF); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++localCount; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + const int idx = base + offset++; + tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, pivotROF, targetROF); } } } } } + } + } + return localCount; + }; + + int dummy{0}; + if (mTaskArena->max_concurrency() <= 1) { + for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { + for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { + forTracklets(PassMode::OnePass{}, iLayer, pivotROF, 0, dummy); + } + } + } else { + bounded_vector> perROFCount(mTrkParams[iteration].TrackletsPerRoad(), bounded_vector(endROF - startROF + 1, 0, mMemoryPool.get()), mMemoryPool.get()); + tbb::parallel_for( + tbb::blocked_range2d(0, mTrkParams[iteration].TrackletsPerRoad(), 1, + startROF, endROF, 1), + [&](auto const& Range) { + for (int iLayer{Range.rows().begin()}; iLayer < Range.rows().end(); ++iLayer) { + for (int pivotROF = Range.cols().begin(); pivotROF < Range.cols().end(); ++pivotROF) { + perROFCount[iLayer][pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, iLayer, pivotROF, 0, dummy); + } + } }); - }); - } - auto sortTracklets = [](const Tracklet& a, const Tracklet& b) -> bool { - return a.firstClusterIndex < b.firstClusterIndex || (a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex < b.secondClusterIndex); - }; - auto equalTracklets = [](const Tracklet& a, const Tracklet& b) -> bool { - return a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex == b.secondClusterIndex; - }; + tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { + std::exclusive_scan(perROFCount[iLayer].begin(), perROFCount[iLayer].end(), perROFCount[iLayer].begin(), 0); + mTimeFrame->getTracklets()[iLayer].resize(perROFCount[iLayer].back()); + }); - mTaskArena.execute([&] { - tbb::parallel_for( - tbb::blocked_range(0, mTrkParams[iteration].CellsPerRoad()), - [&](const tbb::blocked_range& Layers) { - for (int iLayer = Layers.begin(); iLayer < Layers.end(); ++iLayer) { - /// Sort tracklets - auto& trkl{mTimeFrame->getTracklets()[iLayer + 1]}; - tbb::parallel_sort(trkl.begin(), trkl.end(), sortTracklets); - /// Remove duplicates - trkl.erase(std::unique(trkl.begin(), trkl.end(), equalTracklets), trkl.end()); - trkl.shrink_to_fit(); - /// recalculate lut - auto& lut{mTimeFrame->getTrackletsLookupTable()[iLayer]}; - std::fill(lut.begin(), lut.end(), 0); - if (trkl.empty()) { - return; + tbb::parallel_for( + tbb::blocked_range2d(0, mTrkParams[iteration].TrackletsPerRoad(), 1, + startROF, endROF, 1), + [&](auto const& Range) { + for (int iLayer{Range.rows().begin()}; iLayer < Range.rows().end(); ++iLayer) { + if (perROFCount[iLayer].back() == 0) { + continue; + } + for (int pivotROF = Range.cols().begin(); pivotROF < Range.cols().end(); ++pivotROF) { + int baseIdx = perROFCount[iLayer][pivotROF - startROF]; + if (baseIdx == perROFCount[iLayer][pivotROF - startROF + 1]) { + continue; + } + int localIdx = 0; + forTracklets(PassMode::TwoPassInsert{}, iLayer, pivotROF, baseIdx, localIdx); + } } + }); + } + + tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { + /// Sort tracklets + auto& trkl{mTimeFrame->getTracklets()[iLayer]}; + tbb::parallel_sort(trkl.begin(), trkl.end(), [](const Tracklet& a, const Tracklet& b) -> bool { + if (a.firstClusterIndex != b.firstClusterIndex) { + return a.firstClusterIndex < b.firstClusterIndex; + } + return a.secondClusterIndex < b.secondClusterIndex; + }); + /// Remove duplicates + trkl.erase(std::unique(trkl.begin(), trkl.end(), [](const Tracklet& a, const Tracklet& b) -> bool { + return a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex == b.secondClusterIndex; + }), + trkl.end()); + trkl.shrink_to_fit(); + if (iLayer > 0) { /// recalculate lut + auto& lut{mTimeFrame->getTrackletsLookupTable()[iLayer - 1]}; + if (!trkl.empty()) { for (const auto& tkl : trkl) { - lut[tkl.firstClusterIndex]++; + lut[tkl.firstClusterIndex + 1]++; } - std::exclusive_scan(lut.begin(), lut.end(), lut.begin(), 0); - lut.push_back(trkl.size()); + std::inclusive_scan(lut.begin(), lut.end(), lut.begin()); } - }); - }); + } + }); - /// Layer 0 is done outside the loop - // in-place deduplication - auto& trklt0 = mTimeFrame->getTracklets()[0]; - mTaskArena.execute([&] { tbb::parallel_sort(trklt0.begin(), trklt0.end(), sortTracklets); }); - trklt0.erase(std::unique(trklt0.begin(), trklt0.end(), equalTracklets), trklt0.end()); - trklt0.shrink_to_fit(); - - /// Create tracklets labels - if (mTimeFrame->hasMCinformation()) { - for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { - for (auto& trk : mTimeFrame->getTracklets()[iLayer]) { - MCCompLabel label; - int currentId{mTimeFrame->getClusters()[iLayer][trk.firstClusterIndex].clusterId}; - int nextId{mTimeFrame->getClusters()[iLayer + 1][trk.secondClusterIndex].clusterId}; - for (auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { - for (auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; + /// Create tracklets labels + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].createArtefactLabels) { + tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { + for (auto& trk : mTimeFrame->getTracklets()[iLayer]) { + MCCompLabel label; + int currentId{mTimeFrame->getClusters()[iLayer][trk.firstClusterIndex].clusterId}; + int nextId{mTimeFrame->getClusters()[iLayer + 1][trk.secondClusterIndex].clusterId}; + for (const auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { + for (const auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { + if (lab1 == lab2 && lab1.isValid()) { + label = lab1; + break; + } + } + if (label.isValid()) { break; } } - if (label.isValid()) { - break; - } + mTimeFrame->getTrackletsLabel(iLayer).emplace_back(label); } - mTimeFrame->getTrackletsLabel(iLayer).emplace_back(label); - } + }); } - } -} + }); +} // namespace o2::its template void TrackerTraits::computeLayerCells(const int iteration) @@ -270,215 +301,149 @@ void TrackerTraits::computeLayerCells(const int iteration) if (iLayer > 0) { deepVectorClear(mTimeFrame->getCellsLookupTable()[iLayer - 1]); } - if (mTimeFrame->hasMCinformation()) { + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].createArtefactLabels) { deepVectorClear(mTimeFrame->getCellsLabel(iLayer)); } } - mTaskArena.execute([&] { - tbb::parallel_for( - tbb::blocked_range(0, mTrkParams[iteration].CellsPerRoad()), - [&](const tbb::blocked_range& Layers) { - for (int iLayer = Layers.begin(); iLayer < Layers.end(); ++iLayer) { - - if (mTimeFrame->getTracklets()[iLayer + 1].empty() || - mTimeFrame->getTracklets()[iLayer].empty()) { - continue; - } + mTaskArena->execute([&] { + auto forTrackletCells = [&](auto Tag, int iLayer, bounded_vector& layerCells, int iTracklet, int offset = 0) -> int { + const Tracklet& currentTracklet{mTimeFrame->getTracklets()[iLayer][iTracklet]}; + const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; + const int nextLayerFirstTrackletIndex{mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex + 1]}; + int foundCells{0}; + for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { + const Tracklet& nextTracklet{mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet]}; + const auto& nextLbl = mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]; + if (mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet].firstClusterIndex != nextLayerClusterIndex) { + break; + } + if (mTrkParams[iteration].DeltaROF && currentTracklet.getSpanRof(nextTracklet) > mTrkParams[iteration].DeltaROF) { // TODO this has to be improved for the staggering + continue; + } + const float deltaTanLambda{std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; #ifdef OPTIMISATION_OUTPUT - float resolution{o2::gpu::CAMath::Sqrt(0.5f * (mTrkParams[iteration].SystErrorZ2[iLayer] + mTrkParams[iteration].SystErrorZ2[iLayer + 1] + mTrkParams[iteration].SystErrorZ2[iLayer + 2] + mTrkParams[iteration].SystErrorY2[iLayer] + mTrkParams[iteration].SystErrorY2[iLayer + 1] + mTrkParams[iteration].SystErrorY2[iLayer + 2])) / mTrkParams[iteration].LayerResolution[iLayer]}; - resolution = resolution > 1.e-12 ? resolution : 1.f; + float resolution{o2::gpu::CAMath::Sqrt(0.5f * (mTrkParams[iteration].SystErrorZ2[iLayer] + mTrkParams[iteration].SystErrorZ2[iLayer + 1] + mTrkParams[iteration].SystErrorZ2[iLayer + 2] + mTrkParams[iteration].SystErrorY2[iLayer] + mTrkParams[iteration].SystErrorY2[iLayer + 1] + mTrkParams[iteration].SystErrorY2[iLayer + 2])) / mTrkParams[iteration].LayerResolution[iLayer]}; + resolution = resolution > 1.e-12 ? resolution : 1.f; + bool good{mTimeFrame->getTrackletsLabel(iLayer)[iTracklet] == mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]}; + float signedDelta{currentTracklet.tanLambda - nextTracklet.tanLambda}; + off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, good, signedDelta, signedDelta / (mTrkParams[iteration].CellDeltaTanLambdaSigma), tanLambda, resolution) << std::endl; #endif - // count number of cells found - const int currentLayerTrackletsNum{static_cast(mTimeFrame->getTracklets()[iLayer].size())}; - bounded_vector perTrackletCount(currentLayerTrackletsNum + 1, 0, mMemoryPool.get()); - tbb::parallel_for( - tbb::blocked_range(0, currentLayerTrackletsNum), - [&](const tbb::blocked_range& Tracklets) { - for (int iTracklet = Tracklets.begin(); iTracklet < Tracklets.end(); ++iTracklet) { - const Tracklet& currentTracklet{mTimeFrame->getTracklets()[iLayer][iTracklet]}; - const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{ - mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{ - mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex + 1]}; - - if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { - continue; - } + if (deltaTanLambda / mTrkParams[iteration].CellDeltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { - int foundCells{0}; - for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { - if (mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet].firstClusterIndex != nextLayerClusterIndex) { - break; - } - const Tracklet& nextTracklet{mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet]}; - const float deltaTanLambda{std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. + const int clusId[3]{ + mTimeFrame->getClusters()[iLayer][currentTracklet.firstClusterIndex].clusterId, + mTimeFrame->getClusters()[iLayer + 1][nextTracklet.firstClusterIndex].clusterId, + mTimeFrame->getClusters()[iLayer + 2][nextTracklet.secondClusterIndex].clusterId}; + const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[iLayer][clusId[0]]; + const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[iLayer + 1][clusId[1]]; + const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + 2)[clusId[2]]; + auto track{buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf)}; -#ifdef OPTIMISATION_OUTPUT - bool good{mTimeFrame->getTrackletsLabel(iLayer)[iTracklet] == mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]}; - float signedDelta{currentTracklet.tanLambda - nextTracklet.tanLambda}; - off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, good, signedDelta, signedDelta / (mTrkParams[iteration].CellDeltaTanLambdaSigma), tanLambda, resolution) << std::endl; -#endif + float chi2{0.f}; + bool good{false}; + for (int iC{2}; iC--;) { + const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + iC)[clusId[iC]]; - if (deltaTanLambda / mTrkParams[iteration].CellDeltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { - - /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. - const int clusId[3]{ - mTimeFrame->getClusters()[iLayer][currentTracklet.firstClusterIndex].clusterId, - mTimeFrame->getClusters()[iLayer + 1][nextTracklet.firstClusterIndex].clusterId, - mTimeFrame->getClusters()[iLayer + 2][nextTracklet.secondClusterIndex].clusterId}; - const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[iLayer][clusId[0]]; - const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[iLayer + 1][clusId[1]]; - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + 2)[clusId[2]]; - auto track{buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf)}; - - float chi2{0.f}; - bool good{false}; - for (int iC{2}; iC--;) { - const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + iC)[clusId[iC]]; - - if (!track.rotate(trackingHit.alphaTrackingFrame)) { - break; - } - - if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) { - break; - } - - if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { - break; - } - - const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; - if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { - break; - } - - if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { - break; - } - - good = !iC; - chi2 += predChi2; - } - if (good) { - ++foundCells; - } - } - } - perTrackletCount[iTracklet] = foundCells; - } - }); + if (!track.rotate(trackingHit.alphaTrackingFrame)) { + break; + } - // calculate offset table and check if any cells where found - std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); - auto totalCells{perTrackletCount.back()}; - if (totalCells == 0) { - continue; - } - auto& layerCells = mTimeFrame->getCells()[iLayer]; - layerCells.resize(totalCells); - - tbb::parallel_for( - tbb::blocked_range(0, currentLayerTrackletsNum), - [&](const tbb::blocked_range& Tracklets) { - for (int iTracklet = Tracklets.begin(); iTracklet < Tracklets.end(); ++iTracklet) { - if (perTrackletCount[iTracklet] == perTrackletCount[iTracklet + 1]) { - continue; - } + if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) { + break; + } - const Tracklet& currentTracklet{mTimeFrame->getTracklets()[iLayer][iTracklet]}; - const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{ - mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{ - mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex + 1]}; + if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer + iC] * constants::Radl * constants::Rho, true)) { + break; + } - int position = perTrackletCount[iTracklet]; - for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { - if (mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet].firstClusterIndex != nextLayerClusterIndex) { - break; - } - const Tracklet& nextTracklet{mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet]}; - const float deltaTanLambda{std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - - if (deltaTanLambda / mTrkParams[iteration].CellDeltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { - - /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. - const int clusId[3]{ - mTimeFrame->getClusters()[iLayer][currentTracklet.firstClusterIndex].clusterId, - mTimeFrame->getClusters()[iLayer + 1][nextTracklet.firstClusterIndex].clusterId, - mTimeFrame->getClusters()[iLayer + 2][nextTracklet.secondClusterIndex].clusterId}; - const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[iLayer][clusId[0]]; - const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[iLayer + 1][clusId[1]]; - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + 2)[clusId[2]]; - auto track{buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf)}; - - float chi2{0.f}; - bool good{false}; - for (int iC{2}; iC--;) { - const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + iC)[clusId[iC]]; - - if (!track.rotate(trackingHit.alphaTrackingFrame)) { - break; - } - - if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) { - break; - } - - if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { - break; - } - - const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; - if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { - break; - } - - if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { - break; - } - - good = !iC; - chi2 += predChi2; - } - if (good) { - layerCells[position++] = CellSeed(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); - } - } - } - } - }); + const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { + break; + } + + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + break; + } - if (iLayer > 0) { - auto& lut = mTimeFrame->getCellsLookupTable()[iLayer - 1]; - lut.resize(currentLayerTrackletsNum + 1); - std::copy_n(perTrackletCount.begin(), currentLayerTrackletsNum + 1, lut.begin()); + good = !iC; + chi2 += predChi2; + } + if (good) { + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + layerCells.emplace_back(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + layerCells[offset++] = CellSeedN(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); + } else { + static_assert(false, "Unknown mode!"); + } } } - }); - }); + } + return foundCells; + }; - /// Create cells labels - if (mTimeFrame->hasMCinformation()) { - for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - for (auto& cell : mTimeFrame->getCells()[iLayer]) { - MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; - MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; - mTimeFrame->getCellsLabel(iLayer).emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); + tbb::parallel_for(0, mTrkParams[iteration].CellsPerRoad(), [&](const int iLayer) { + if (mTimeFrame->getTracklets()[iLayer + 1].empty() || + mTimeFrame->getTracklets()[iLayer].empty()) { + return; } - } - } - if constexpr (debugLevel) { - for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - std::cout << "Cells on layer " << iLayer << " " << mTimeFrame->getCells()[iLayer].size() << std::endl; + auto& layerCells = mTimeFrame->getCells()[iLayer]; + const int currentLayerTrackletsNum{static_cast(mTimeFrame->getTracklets()[iLayer].size())}; + bounded_vector perTrackletCount(currentLayerTrackletsNum + 1, 0, mMemoryPool.get()); + if (mTaskArena->max_concurrency() <= 1) { + for (int iTracklet{0}; iTracklet < currentLayerTrackletsNum; ++iTracklet) { + perTrackletCount[iTracklet] = forTrackletCells(PassMode::OnePass{}, iLayer, layerCells, iTracklet); + } + std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); + } else { + tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) { + perTrackletCount[iTracklet] = forTrackletCells(PassMode::TwoPassCount{}, iLayer, layerCells, iTracklet); + }); + + std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); + auto totalCells{perTrackletCount.back()}; + if (totalCells == 0) { + return; + } + layerCells.resize(totalCells); + + tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) { + int offset = perTrackletCount[iTracklet]; + if (offset == perTrackletCount[iTracklet + 1]) { + return; + } + forTrackletCells(PassMode::TwoPassInsert{}, iLayer, layerCells, iTracklet, offset); + }); + } + + if (iLayer > 0) { + auto& lut = mTimeFrame->getCellsLookupTable()[iLayer - 1]; + lut.resize(currentLayerTrackletsNum + 1); + std::copy_n(perTrackletCount.begin(), currentLayerTrackletsNum + 1, lut.begin()); + } + }); + + /// Create cells labels + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].createArtefactLabels) { + tbb::parallel_for(0, mTrkParams[iteration].CellsPerRoad(), [&](const int iLayer) { + mTimeFrame->getCellsLabel(iLayer).reserve(mTimeFrame->getCells()[iLayer].size()); + for (const auto& cell : mTimeFrame->getCells()[iLayer]) { + MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; + MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; + mTimeFrame->getCellsLabel(iLayer).emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); + } + }); } - } + }); } template @@ -487,132 +452,133 @@ void TrackerTraits::findCellsNeighbours(const int iteration) #ifdef OPTIMISATION_OUTPUT std::ofstream off(std::format("cellneighs{}.txt", iteration)); #endif - for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad() - 1; ++iLayer) { - const int nextLayerCellsNum{static_cast(mTimeFrame->getCells()[iLayer + 1].size())}; - deepVectorClear(mTimeFrame->getCellsNeighbours()[iLayer]); - deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[iLayer]); - if (mTimeFrame->getCells()[iLayer + 1].empty() || - mTimeFrame->getCellsLookupTable()[iLayer].empty()) { - continue; - } - mTaskArena.execute([&] { - int layerCellsNum{static_cast(mTimeFrame->getCells()[iLayer].size())}; + struct Neighbor { + int cell{-1}, nextCell{-1}, level{-1}; + }; - bounded_vector perCellCount(layerCellsNum + 1, 0, mMemoryPool.get()); - tbb::parallel_for( - tbb::blocked_range(0, layerCellsNum), - [&](const tbb::blocked_range& Cells) { - for (int iCell = Cells.begin(); iCell < Cells.end(); ++iCell) { - const auto& currentCellSeed{mTimeFrame->getCells()[iLayer][iCell]}; - const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; - const int nextLayerFirstCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex + 1]}; - - int foundNextCells{0}; - for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - auto nextCellSeed{mTimeFrame->getCells()[iLayer + 1][iNextCell]}; /// copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { - break; - } + mTaskArena->execute([&] { + for (int iLayer{0}; iLayer < mTrkParams[iteration].NeighboursPerRoad(); ++iLayer) { + deepVectorClear(mTimeFrame->getCellsNeighbours()[iLayer]); + deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[iLayer]); + if (mTimeFrame->getCells()[iLayer + 1].empty() || + mTimeFrame->getCellsLookupTable()[iLayer].empty()) { + continue; + } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || - !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { - continue; - } - float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation + int nCells{static_cast(mTimeFrame->getCells()[iLayer].size())}; + bounded_vector cellsNeighbours(mMemoryPool.get()); -#ifdef OPTIMISATION_OUTPUT - bool good{mTimeFrame->getCellsLabel(iLayer)[iCell] == mTimeFrame->getCellsLabel(iLayer + 1)[iNextCell]}; - off << std::format("{}\t{:d}\t{}", iLayer, good, chi2) << std::endl; -#endif + auto forCellNeighbour = [&](auto Tag, int iCell, int offset = 0) -> int { + const auto& currentCellSeed{mTimeFrame->getCells()[iLayer][iCell]}; + const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; + const int nextLayerFirstCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex]}; + const int nextLayerLastCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex + 1]}; + int foundNextCells{0}; + for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { + auto nextCellSeed{mTimeFrame->getCells()[iLayer + 1][iNextCell]}; /// copy + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { + break; + } - if (chi2 > mTrkParams[0].MaxChi2ClusterAttachment) { - continue; - } - ++foundNextCells; + if (mTrkParams[iteration].DeltaROF) { // TODO this has to be improved for the staggering + const auto& trkl00 = mTimeFrame->getTracklets()[iLayer][currentCellSeed.getFirstTrackletIndex()]; + const auto& trkl01 = mTimeFrame->getTracklets()[iLayer + 1][currentCellSeed.getSecondTrackletIndex()]; + const auto& trkl10 = mTimeFrame->getTracklets()[iLayer + 1][nextCellSeed.getFirstTrackletIndex()]; + const auto& trkl11 = mTimeFrame->getTracklets()[iLayer + 2][nextCellSeed.getSecondTrackletIndex()]; + if ((std::max({trkl00.getMaxRof(), trkl01.getMaxRof(), trkl10.getMaxRof(), trkl11.getMaxRof()}) - + std::min({trkl00.getMinRof(), trkl01.getMinRof(), trkl10.getMinRof(), trkl11.getMinRof()})) > mTrkParams[0].DeltaROF) { + continue; } - perCellCount[iCell] = foundNextCells; } - }); - std::exclusive_scan(perCellCount.begin(), perCellCount.end(), perCellCount.begin(), 0); - int totalCellNeighbours = perCellCount.back(); - if (totalCellNeighbours == 0) { - deepVectorClear(mTimeFrame->getCellsNeighbours()[iLayer]); - return; - } + if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || + !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { + continue; + } + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation - struct Neighbor { - int cell{-1}, nextCell{-1}, level{-1}; - }; - bounded_vector cellsNeighbours(mMemoryPool.get()); - cellsNeighbours.resize(totalCellNeighbours); +#ifdef OPTIMISATION_OUTPUT + bool good{mTimeFrame->getCellsLabel(iLayer)[iCell] == mTimeFrame->getCellsLabel(iLayer + 1)[iNextCell]}; + off << std::format("{}\t{:d}\t{}", iLayer, good, chi2) << std::endl; +#endif - tbb::parallel_for( - tbb::blocked_range(0, layerCellsNum), - [&](const tbb::blocked_range& Cells) { - for (int iCell = Cells.begin(); iCell < Cells.end(); ++iCell) { - if (perCellCount[iCell] == perCellCount[iCell + 1]) { - continue; - } - const auto& currentCellSeed{mTimeFrame->getCells()[iLayer][iCell]}; - const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; - const int nextLayerFirstCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex + 1]}; - - int position = perCellCount[iCell]; - for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - auto nextCellSeed{mTimeFrame->getCells()[iLayer + 1][iNextCell]}; /// copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { - break; - } + if (chi2 > mTrkParams[0].MaxChi2ClusterAttachment) { + continue; + } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || - !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { - continue; - } + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + cellsNeighbours.emplace_back(iCell, iNextCell, currentCellSeed.getLevel() + 1); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundNextCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + cellsNeighbours[offset++] = {iCell, iNextCell, currentCellSeed.getLevel() + 1}; + } else { + static_assert(false, "Unknown mode!"); + } + } + return foundNextCells; + }; - float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation - if (chi2 > mTrkParams[0].MaxChi2ClusterAttachment) { - continue; - } + if (mTaskArena->max_concurrency() <= 1) { + for (int iCell{0}; iCell < nCells; ++iCell) { + forCellNeighbour(PassMode::OnePass{}, iCell); + } + } else { + bounded_vector perCellCount(nCells + 1, 0, mMemoryPool.get()); + tbb::parallel_for(0, nCells, [&](const int iCell) { + perCellCount[iCell] = forCellNeighbour(PassMode::TwoPassCount{}, iCell); + }); - cellsNeighbours[position++] = {iCell, iNextCell, currentCellSeed.getLevel() + 1}; - } + std::exclusive_scan(perCellCount.begin(), perCellCount.end(), perCellCount.begin(), 0); + int totalCellNeighbours = perCellCount.back(); + if (totalCellNeighbours == 0) { + deepVectorClear(mTimeFrame->getCellsNeighbours()[iLayer]); + continue; + } + cellsNeighbours.resize(totalCellNeighbours); + + tbb::parallel_for(0, nCells, [&](const int iCell) { + int offset = perCellCount[iCell]; + if (offset == perCellCount[iCell + 1]) { + return; } + forCellNeighbour(PassMode::TwoPassInsert{}, iCell, offset); }); + } + + if (cellsNeighbours.empty()) { + continue; + } tbb::parallel_sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](const auto& a, const auto& b) { return a.nextCell < b.nextCell; }); auto& cellsNeighbourLUT = mTimeFrame->getCellsNeighboursLUT()[iLayer]; - cellsNeighbourLUT.assign(nextLayerCellsNum, 0); + cellsNeighbourLUT.assign(mTimeFrame->getCells()[iLayer + 1].size(), 0); for (const auto& neigh : cellsNeighbours) { ++cellsNeighbourLUT[neigh.nextCell]; } std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin()); - mTimeFrame->getCellsNeighbours()[iLayer].reserve(totalCellNeighbours); + mTimeFrame->getCellsNeighbours()[iLayer].reserve(cellsNeighbours.size()); std::ranges::transform(cellsNeighbours, std::back_inserter(mTimeFrame->getCellsNeighbours()[iLayer]), [](const auto& neigh) { return neigh.cell; }); - auto it = cellsNeighbours.begin(); - while (it != cellsNeighbours.end()) { - const int current_nextCell = it->nextCell; - auto group_end = std::find_if_not(it, cellsNeighbours.end(), - [current_nextCell](const auto& nb) { return nb.nextCell == current_nextCell; }); - const auto max_level_it = std::max_element(it, group_end, - [](const auto& a, const auto& b) { return a.level < b.level; }); - mTimeFrame->getCells()[iLayer + 1][current_nextCell].setLevel(max_level_it->level); - it = group_end; + for (auto it = cellsNeighbours.begin(); it != cellsNeighbours.end();) { + int cellIdx = it->nextCell; + int maxLvl = it->level; + while (++it != cellsNeighbours.end() && it->nextCell == cellIdx) { + maxLvl = std::max(maxLvl, it->level); + } + mTimeFrame->getCells()[iLayer + 1][cellIdx].setLevel(maxLvl); } - }); - } + } + }); } template -void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) +void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) { CA_DEBUGGER(std::cout << "Processing neighbours layer " << iLayer << " level " << iLevel << ", size of the cell seeds: " << currentCellSeed.size() << std::endl); auto propagator = o2::base::Propagator::Instance(); @@ -621,141 +587,121 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou int failed[5]{0, 0, 0, 0, 0}, attempts{0}, failedByMismatch{0}; #endif - mTaskArena.execute([&] { - bounded_vector perCellCount(currentCellSeed.size() + 1, 0, mMemoryPool.get()); - tbb::parallel_for( - tbb::blocked_range(0, (int)currentCellSeed.size()), - [&](const tbb::blocked_range& Cells) { - for (int iCell = Cells.begin(); iCell < Cells.end(); ++iCell) { - const CellSeed& currentCell{currentCellSeed[iCell]}; - int foundSeeds{0}; - if (currentCell.getLevel() != iLevel) { - continue; - } - if (currentCellId.empty() && (mTimeFrame->isClusterUsed(iLayer, currentCell.getFirstClusterIndex()) || - mTimeFrame->isClusterUsed(iLayer + 1, currentCell.getSecondClusterIndex()) || - mTimeFrame->isClusterUsed(iLayer + 2, currentCell.getThirdClusterIndex()))) { - continue; /// this we do only on the first iteration, hence the check on currentCellId - } - const int cellId = currentCellId.empty() ? iCell : currentCellId[iCell]; - const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId - 1] : 0}; - const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId]}; - - for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { - CA_DEBUGGER(attempts++); - const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; - const CellSeed& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; - if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { - CA_DEBUGGER(failedByMismatch++); - continue; - } - if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { - continue; - } - if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { - CA_DEBUGGER(failed[0]++); - continue; - } - /// Let's start the fitting procedure - CellSeed seed{currentCell}; - auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer - 1)[neighbourCell.getFirstClusterIndex()]; + mTaskArena->execute([&] { + auto forCellNeighbours = [&](auto Tag, int iCell, int offset = 0) -> int { + const auto& currentCell{currentCellSeed[iCell]}; - if (!seed.rotate(trHit.alphaTrackingFrame)) { - CA_DEBUGGER(failed[1]++); - continue; - } + if constexpr (decltype(Tag)::value != PassMode::TwoPassInsert::value) { + if (currentCell.getLevel() != iLevel) { + return 0; + } + if (currentCellId.empty() && (mTimeFrame->isClusterUsed(iLayer, currentCell.getFirstClusterIndex()) || + mTimeFrame->isClusterUsed(iLayer + 1, currentCell.getSecondClusterIndex()) || + mTimeFrame->isClusterUsed(iLayer + 2, currentCell.getThirdClusterIndex()))) { + return 0; /// this we do only on the first iteration, hence the check on currentCellId + } + } - if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mCorrType)) { - CA_DEBUGGER(failed[2]++); - continue; - } + const int cellId = currentCellId.empty() ? iCell : currentCellId[iCell]; + const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId - 1] : 0}; + const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId]}; + int foundSeeds{0}; + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + CA_DEBUGGER(attempts++); + const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; + const auto& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; + if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { + CA_DEBUGGER(failedByMismatch++); + continue; + } + if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { + continue; + } + if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { + CA_DEBUGGER(failed[0]++); + continue; + } - if (mCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!seed.correctForMaterial(mTrkParams[0].LayerxX0[iLayer - 1], mTrkParams[0].LayerxX0[iLayer - 1] * constants::Radl * constants::Rho, true)) { - continue; - } - } + /// Let's start the fitting procedure + CellSeedN seed{currentCell}; + const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer - 1)[neighbourCell.getFirstClusterIndex()]; - auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; - if ((predChi2 > mTrkParams[0].MaxChi2ClusterAttachment) || predChi2 < 0.f) { - CA_DEBUGGER(failed[3]++); - continue; - } - seed.setChi2(seed.getChi2() + predChi2); - if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { - CA_DEBUGGER(failed[4]++); - continue; - } - ++foundSeeds; - } - perCellCount[iCell] = foundSeeds; + if (!seed.rotate(trHit.alphaTrackingFrame)) { + CA_DEBUGGER(failed[1]++); + continue; } - }); - std::exclusive_scan(perCellCount.begin(), perCellCount.end(), perCellCount.begin(), 0); - auto totalNeighbours{perCellCount.back()}; - if (totalNeighbours == 0) { - return; - } - updatedCellSeeds.resize(totalNeighbours); - updatedCellsIds.resize(totalNeighbours); - - tbb::parallel_for( - tbb::blocked_range(0, (int)currentCellSeed.size()), - [&](const tbb::blocked_range& Cells) { - for (int iCell = Cells.begin(); iCell < Cells.end(); ++iCell) { - if (perCellCount[iCell] == perCellCount[iCell + 1]) { + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[0].CorrType)) { + CA_DEBUGGER(failed[2]++); + continue; + } + + if (mTrkParams[0].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(mTrkParams[0].LayerxX0[iLayer - 1], mTrkParams[0].LayerxX0[iLayer - 1] * constants::Radl * constants::Rho, true)) { continue; } - // no need for further checks on cell level - - const CellSeed& currentCell{currentCellSeed[iCell]}; - const int cellId = currentCellId.empty() ? iCell : currentCellId[iCell]; - const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId - 1] : 0}; - const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId]}; - - int offset = perCellCount[iCell]; - for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { - const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; - const CellSeed& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; - if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex() || - mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex()) || - currentCell.getLevel() - 1 != neighbourCell.getLevel()) { - continue; - } + } - auto seed = currentCell; + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > mTrkParams[0].MaxChi2ClusterAttachment) || predChi2 < 0.f) { + CA_DEBUGGER(failed[3]++); + continue; + } + seed.setChi2(seed.getChi2() + predChi2); + if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { + CA_DEBUGGER(failed[4]++); + continue; + } - const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer - 1)[neighbourCell.getFirstClusterIndex()]; - if (!seed.rotate(trHit.alphaTrackingFrame) || !propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mCorrType)) { - continue; - } + if constexpr (decltype(Tag)::value != PassMode::TwoPassCount::value) { + seed.getClusters()[iLayer - 1] = neighbourCell.getFirstClusterIndex(); + seed.setLevel(neighbourCell.getLevel()); + seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); + seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); + } - if (mCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!seed.correctForMaterial(mTrkParams[0].LayerxX0[iLayer - 1], mTrkParams[0].LayerxX0[iLayer - 1] * constants::Radl * constants::Rho, true)) { - continue; - } - } + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + updatedCellSeeds.push_back(seed); + updatedCellsIds.push_back(neighbourCellId); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundSeeds; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + updatedCellSeeds[offset] = seed; + updatedCellsIds[offset++] = neighbourCellId; + } else { + static_assert(false, "Unknown mode!"); + } + } + return foundSeeds; + }; - auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; - if ((predChi2 > mTrkParams[0].MaxChi2ClusterAttachment) || predChi2 < 0.f) { - continue; - } - seed.setChi2(seed.getChi2() + predChi2); - if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { - continue; - } + const int nCells = static_cast(currentCellSeed.size()); + if (mTaskArena->max_concurrency() <= 1) { + for (int iCell{0}; iCell < nCells; ++iCell) { + forCellNeighbours(PassMode::OnePass{}, iCell); + } + } else { + bounded_vector perCellCount(nCells + 1, 0, mMemoryPool.get()); + tbb::parallel_for(0, nCells, [&](const int iCell) { + perCellCount[iCell] = forCellNeighbours(PassMode::TwoPassCount{}, iCell); + }); - seed.getClusters()[iLayer - 1] = neighbourCell.getFirstClusterIndex(); - seed.setLevel(neighbourCell.getLevel()); - seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); - seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); + std::exclusive_scan(perCellCount.begin(), perCellCount.end(), perCellCount.begin(), 0); + auto totalNeighbours{perCellCount.back()}; + if (totalNeighbours == 0) { + return; + } + updatedCellSeeds.resize(totalNeighbours); + updatedCellsIds.resize(totalNeighbours); - updatedCellSeeds[offset] = seed; - updatedCellsIds[offset++] = neighbourCellId; - } + tbb::parallel_for(0, nCells, [&](const int iCell) { + int offset = perCellCount[iCell]; + if (offset == perCellCount[iCell + 1]) { + return; } + forCellNeighbours(PassMode::TwoPassInsert{}, iCell, offset); }); + } }); #ifdef CA_DEBUG @@ -772,21 +718,24 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou template void TrackerTraits::findRoads(const int iteration) { - CA_DEBUGGER(std::cout << "Finding roads, iteration " << iteration << std::endl); - + bounded_vector> firstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); + bounded_vector> sharedFirstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); + firstClusters.resize(mTrkParams[iteration].NLayers); + sharedFirstClusters.resize(mTrkParams[iteration].NLayers); for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) { - CA_DEBUGGER(std::cout << "\t > Processing level " << startLevel << std::endl); - auto seedFilter = [&](const CellSeed& seed) { + + auto seedFilter = [&](const auto& seed) { return seed.getQ2Pt() <= 1.e3 && seed.getChi2() <= mTrkParams[0].MaxChi2NDF * ((startLevel + 2) * 2 - 5); }; - bounded_vector trackSeeds(mMemoryPool.get()); - for (int startLayer{mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= startLevel - 1; --startLayer) { + + bounded_vector trackSeeds(mMemoryPool.get()); + for (int startLayer{mTrkParams[iteration].NeighboursPerRoad()}; startLayer >= startLevel - 1; --startLayer) { if ((mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { continue; } - CA_DEBUGGER(std::cout << "\t\t > Starting processing layer " << startLayer << std::endl); + bounded_vector lastCellId(mMemoryPool.get()), updatedCellId(mMemoryPool.get()); - bounded_vector lastCellSeed(mMemoryPool.get()), updatedCellSeed(mMemoryPool.get()); + bounded_vector lastCellSeed(mMemoryPool.get()), updatedCellSeed(mMemoryPool.get()); processNeighbours(startLayer, startLevel, mTimeFrame->getCells()[startLayer], lastCellId, updatedCellSeed, updatedCellId); @@ -812,66 +761,76 @@ void TrackerTraits::findRoads(const int iteration) } bounded_vector tracks(mMemoryPool.get()); - mTaskArena.execute([&] { - bounded_vector perSeedCount(trackSeeds.size() + 1, 0, mMemoryPool.get()); - tbb::parallel_for( - tbb::blocked_range(0, (int)trackSeeds.size()), - [&](const tbb::blocked_range& Seeds) { - for (int iSeed = Seeds.begin(); iSeed < Seeds.end(); ++iSeed) { - const CellSeed& seed{trackSeeds[iSeed]}; - TrackITSExt temporaryTrack{seed}; - temporaryTrack.resetCovariance(); - temporaryTrack.setChi2(0); - for (int iL{0}; iL < 7; ++iL) { - temporaryTrack.setExternalClusterIndex(iL, seed.getCluster(iL), seed.getCluster(iL) != constants::UnusedIndex); - } - - bool fitSuccess = fitTrack(temporaryTrack, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF); - if (!fitSuccess) { - continue; - } - temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); - temporaryTrack.resetCovariance(); - temporaryTrack.setChi2(0); - fitSuccess = fitTrack(temporaryTrack, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.f); - if (!fitSuccess || temporaryTrack.getPt() < mTrkParams[iteration].MinPt[mTrkParams[iteration].NLayers - temporaryTrack.getNClusters()]) { - continue; - } - ++perSeedCount[iSeed]; + mTaskArena->execute([&] { + auto forSeed = [&](auto Tag, int iSeed, int offset = 0) { + TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iSeed]); + o2::track::TrackPar linRef{temporaryTrack}; + bool fitSuccess = fitTrack(temporaryTrack, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, o2::constants::math::VeryBig, 0, &linRef); + if (!fitSuccess) { + return 0; + } + 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, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.f, 0, &linRef); + if (!fitSuccess || temporaryTrack.getPt() < mTrkParams[iteration].MinPt[mTrkParams[iteration].NLayers - temporaryTrack.getNClusters()]) { + return 0; + } + if (mTrkParams[0].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, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, o2::constants::math::VeryBig, 0, &linRef); + if (!fitSuccess) { + return 0; } + temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); + temporaryTrack.getParamIn() = saveInw; + temporaryTrack.setChi2(saveChi2); + } + + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + tracks.push_back(temporaryTrack); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + // nothing to do + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + tracks[offset] = temporaryTrack; + } else { + static_assert(false, "Unknown mode!"); + } + return 1; + }; + + const int nSeeds = static_cast(trackSeeds.size()); + if (mTaskArena->max_concurrency() <= 1) { + for (int iSeed{0}; iSeed < nSeeds; ++iSeed) { + forSeed(PassMode::OnePass{}, iSeed); + } + } else { + bounded_vector perSeedCount(nSeeds + 1, 0, mMemoryPool.get()); + tbb::parallel_for(0, nSeeds, [&](const int iSeed) { + perSeedCount[iSeed] = forSeed(PassMode::TwoPassCount{}, iSeed); }); - std::exclusive_scan(perSeedCount.begin(), perSeedCount.end(), perSeedCount.begin(), 0); - auto totalTracks{perSeedCount.back()}; - if (totalTracks == 0) { - return; - } - tracks.resize(totalTracks); - tbb::parallel_for( - tbb::blocked_range(0, (int)trackSeeds.size()), - [&](const tbb::blocked_range& Seeds) { - for (int iSeed = Seeds.begin(); iSeed < Seeds.end(); ++iSeed) { - if (perSeedCount[iSeed] == perSeedCount[iSeed + 1]) { - continue; - } - const CellSeed& seed{trackSeeds[iSeed]}; - auto& trk = tracks[perSeedCount[iSeed]] = TrackITSExt(seed); - trk.resetCovariance(); - trk.setChi2(0); - for (int iL{0}; iL < 7; ++iL) { - trk.setExternalClusterIndex(iL, seed.getCluster(iL), seed.getCluster(iL) != constants::UnusedIndex); - } + std::exclusive_scan(perSeedCount.begin(), perSeedCount.end(), perSeedCount.begin(), 0); + auto totalTracks{perSeedCount.back()}; + if (totalTracks == 0) { + return; + } + tracks.resize(totalTracks); - bool fitSuccess = fitTrack(trk, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF); - if (!fitSuccess) { - continue; - } - trk.getParamOut() = trk.getParamIn(); - trk.resetCovariance(); - trk.setChi2(0); - fitTrack(trk, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.f); + tbb::parallel_for(0, nSeeds, [&](const int iSeed) { + if (perSeedCount[iSeed] == perSeedCount[iSeed + 1]) { + return; } + forSeed(PassMode::TwoPassInsert{}, iSeed, perSeedCount[iSeed]); }); + } deepVectorClear(trackSeeds); tbb::parallel_sort(tracks.begin(), tracks.end(), [](const auto& a, const auto& b) { @@ -882,15 +841,22 @@ void TrackerTraits::findRoads(const int iteration) for (auto& track : tracks) { int nShared = 0; bool isFirstShared{false}; + int firstLayer{-1}, firstCluster{-1}; for (int iLayer{0}; iLayer < 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)); + bool isShared = mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + nShared += int(isShared); + if (firstLayer < 0) { + firstCluster = track.getClusterIndex(iLayer); + isFirstShared = isShared && mTrkParams[0].AllowSharingFirstCluster && std::find(firstClusters[iLayer].begin(), firstClusters[iLayer].end(), firstCluster) != firstClusters[iLayer].end(); + firstLayer = iLayer; + } } - if (nShared > mTrkParams[0].ClusterSharing) { + /// do not account for the first cluster in the shared clusters number if it is allowed + if (nShared - int(isFirstShared && mTrkParams[0].AllowSharingFirstCluster) > mTrkParams[0].ClusterSharing) { continue; } @@ -919,6 +885,33 @@ void TrackerTraits::findRoads(const int iteration) track.setNextROFbit(); } mTimeFrame->getTracks(o2::gpu::CAMath::Min(rofs[0], rofs[1])).emplace_back(track); + + firstClusters[firstLayer].push_back(firstCluster); + if (isFirstShared) { + sharedFirstClusters[firstLayer].push_back(firstCluster); + } + } + } + + /// Now we have to set the shared cluster flag + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { + std::sort(sharedFirstClusters[iLayer].begin(), sharedFirstClusters[iLayer].end()); + } + + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + for (auto& track : mTimeFrame->getTracks(iROF)) { + int firstLayer{mTrkParams[0].NLayers}, firstCluster{constants::UnusedIndex}; + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + firstLayer = iLayer; + firstCluster = track.getClusterIndex(iLayer); + break; + } + if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { + track.setSharedClusters(); + } } } } @@ -1062,7 +1055,7 @@ void TrackerTraits::findShortPrimaries() } template -bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut, float chi2ndfcut, float maxQoverPt, int nCl) +bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut, float chi2ndfcut, float maxQoverPt, int nCl, o2::track::TrackPar* linRef) { auto propInstance = o2::base::Propagator::Instance(); @@ -1071,21 +1064,31 @@ bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, in continue; } const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer)[track.getClusterIndex(iLayer)]; - - if (!track.rotate(trackingHit.alphaTrackingFrame)) { - return false; - } - - if (!propInstance->propagateToX(track, trackingHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mCorrType)) { - return false; - } - - if (mCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { - continue; + if (linRef) { + if (!track.rotate(trackingHit.alphaTrackingFrame, *linRef, getBz())) { + return false; + } + if (!propInstance->propagateToX(track, *linRef, trackingHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[0].CorrType)) { + return false; + } + if (mTrkParams[0].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!track.correctForMaterial(*linRef, mTrkParams[0].LayerxX0[iLayer], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + continue; + } + } + } else { + if (!track.rotate(trackingHit.alphaTrackingFrame)) { + return false; + } + if (!propInstance->propagateToX(track, trackingHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[0].CorrType)) { + return false; + } + if (mTrkParams[0].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + continue; + } } } - auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; if ((nCl >= 3 && predChi2 > chi2clcut) || predChi2 < 0.f) { return false; @@ -1094,6 +1097,10 @@ bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, in if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { return false; } + if (linRef && mTrkParams[0].ShiftRefToCluster) { // displace the reference to the last updated cluster + linRef->setY(trackingHit.positionTrackingFrame[0]); + linRef->setZ(trackingHit.positionTrackingFrame[1]); + } nCl++; } return std::abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); @@ -1211,36 +1218,82 @@ bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool ou return swapped; } +// create a new seed either from the existing track inner param or reseed from the edgepointd and cluster in the middle +template +TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) +{ + 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) { + lrMin = o2::gpu::CAMath::Min(lrMin, iL); + lrMax = o2::gpu::CAMath::Max(lrMax, iL); + } + } + int ncl = temporaryTrack.getNClusters(); + if (ncl < mTrkParams[0].ReseedIfShorter) { // reseed with circle passing via edges and the midpoint + if (ncl == mTrkParams[0].NLayers) { + lrMin = 0; + lrMax = mTrkParams[0].NLayers - 1; + lrMid = (lrMin + lrMax) / 2; + } else { + lrMid = lrMin + 1; + float midR = 0.5 * (mTrkParams[0].LayerRadii[lrMax] + mTrkParams[0].LayerRadii[lrMin]), dstMidR = o2::gpu::GPUCommonMath::Abs(midR - mTrkParams[0].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 - mTrkParams[0].LayerRadii[iL]); + if (dst < dstMidR) { + lrMid = iL; + dstMidR = dst; + } + } + } + const auto& cluster0_tf = mTimeFrame->getTrackingFrameInfoOnLayer(lrMin)[seed.getCluster(lrMin)]; // if the sensor frame! + const auto& cluster1_gl = mTimeFrame->getUnsortedClusters()[lrMid][seed.getCluster(lrMid)]; // global frame + const auto& cluster2_gl = mTimeFrame->getUnsortedClusters()[lrMax][seed.getCluster(lrMax)]; // global frame + temporaryTrack.getParamIn() = buildTrackSeed(cluster2_gl, cluster1_gl, cluster0_tf, true); + } + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + return temporaryTrack; +} + /// Clusters are given from inside outward (cluster3 is the outermost). The outermost cluster is given in the tracking /// frame coordinates whereas the others are referred to the global frame. template -track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3) +track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3, bool reverse) { - float ca{-999.f}, sa{-999.f}; + 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 z1 = cluster1.zCoordinate; const float x2 = cluster2.xCoordinate * ca + cluster2.yCoordinate * sa; const float y2 = -cluster2.xCoordinate * sa + cluster2.yCoordinate * ca; - const float z2 = cluster2.zCoordinate; const float x3 = tf3.xTrackingFrame; const float y3 = tf3.positionTrackingFrame[0]; - const float z3 = tf3.positionTrackingFrame[1]; - float tgp{1.f}, crv{1.f}, snp{-999.f}, tgl12{-999.f}, tgl23{-999.f}, q2pt{1.f / track::kMostProbablePt}, q2pt2{1.f}, sg2q2pt{-999.f}; + + float snp, q2pt, q2pt2; if (mIsZeroField) { - tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); - snp = tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + 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 { - crv = math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); - snp = crv * (x3 - math_utils::computeCurvatureCentreX(x3, y3, x2, y2, x1, y1)); - q2pt = crv / (mBz * o2::constants::math::B2C); + 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 / (mBz * o2::constants::math::B2C); q2pt2 = crv * crv; } - tgl12 = math_utils::computeTanDipAngle(x1, y1, x2, y2, z1, z2); - tgl23 = math_utils::computeTanDipAngle(x2, y2, x3, y3, z2, z3); - sg2q2pt = track::kC1Pt2max * (q2pt2 > 0.0005f ? (q2pt2 < 1.f ? q2pt2 : 1.f) : 0.0005f); - return {tf3.xTrackingFrame, tf3.alphaTrackingFrame, {y3, z3, snp, 0.5f * (tgl12 + tgl23), 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}}; + + 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 @@ -1254,23 +1307,29 @@ void TrackerTraits::setBz(float bz) template bool TrackerTraits::isMatLUT() const { - return o2::base::Propagator::Instance()->getMatLUT() && (mCorrType == o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); + return o2::base::Propagator::Instance()->getMatLUT() && (mTrkParams[0].CorrType == o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); } template -void TrackerTraits::setNThreads(int n) +void TrackerTraits::setNThreads(int n, std::shared_ptr& arena) { - if (mNThreads == n && mTaskArena.is_active()) { - return; - } - mNThreads = n > 0 ? n : 1; #if defined(OPTIMISATION_OUTPUT) || defined(CA_DEBUG) - mNThreads = 1; // only works while serial + mTaskArena = std::make_shared(1); +#else + if (arena == nullptr) { + mTaskArena = std::make_shared(std::abs(n)); + LOGP(info, "Setting tracker with {} threads.", n); + } else { + mTaskArena = arena; + LOGP(info, "Attaching tracker to calling thread's arena"); + } #endif - mTaskArena.initialize(mNThreads); - LOGP(info, "Setting tracker with {} threads.", mNThreads); } template class TrackerTraits<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class TrackerTraits<11>; +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx index b5fbedcc89339..3101c34d4ab8f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx @@ -9,8 +9,36 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include + +#include "Framework/Logger.h" #include "ITStracking/TrackingConfigParam.h" O2ParamImpl(o2::its::VertexerParamConfig); O2ParamImpl(o2::its::TrackerParamConfig); O2ParamImpl(o2::its::ITSGpuTrackingParamConfig); + +namespace o2::its +{ + +void ITSGpuTrackingParamConfig::maybeOverride() const +{ + if (nBlocks == OverrideValue && nThreads == OverrideValue) { + return; + } + const auto name = getName(); + auto members = getDataMembers(); + for (auto member : *members) { + if (!member.name.ends_with(BlocksName) && !member.name.ends_with(ThreadsName)) { + if (nBlocks != OverrideValue && member.name.starts_with(BlocksName) && (member.value != nBlocks)) { + o2::conf::ConfigurableParam::setValue(name, member.name, nBlocks); + } + if (nThreads != OverrideValue && member.name.starts_with(ThreadsName) && (member.value != nThreads)) { + o2::conf::ConfigurableParam::setValue(name, member.name, nThreads); + } + } + } + LOGP(info, "Overwriting gpu threading parameters"); +} // namespace o2::its + +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index b6b4796690905..d5f13cd9d25ea 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -9,14 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include + +#include + #include "ITSMFTBase/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITSReconstruction/FastMultEstConfig.h" #include "ITSReconstruction/FastMultEst.h" +#include "ITStracking/TrackingConfigParam.h" #include "ITStracking/TrackingInterface.h" -#include #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/PhysTrigger.h" @@ -24,7 +28,6 @@ #include "CommonDataFormat/IRFrame.h" #include "DetectorsBase/GRPGeomHelper.h" #include "ITStracking/BoundedAllocator.h" -#include "ITStracking/TrackingConfigParam.h" #include "Framework/DeviceSpec.h" using namespace o2::framework; @@ -32,122 +35,40 @@ using namespace o2::its; void ITSTrackingInterface::initialise() { - mRunVertexer = true; - mCosmicsProcessing = false; - std::vector vertParams; - std::vector trackParams; - const auto& vertConf = o2::its::VertexerParamConfig::Instance(); + // get parameters const auto& trackConf = o2::its::TrackerParamConfig::Instance(); - float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791; - float bFactorTracklets = bFactor < 0.01 ? 1. : bFactor; // for tracklets only - if (mMode == TrackingMode::Unset) { - mMode = (TrackingMode)(trackConf.trackingMode); - LOGP(info, "Tracking mode not set, trying to fetch it from configurable params to: {}", asString(mMode)); - } - if (mMode == TrackingMode::Async) { - trackParams.resize(trackConf.doUPCIteration ? 4 : 3); - vertParams.resize(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice - trackParams[1].TrackletMinPt = 0.2f; - trackParams[1].CellDeltaTanLambdaSigma *= 2.; - trackParams[2].TrackletMinPt = 0.1f; - trackParams[2].CellDeltaTanLambdaSigma *= 4.; - - trackParams[0].MinPt[0] = 1.f / 12; // 7cl - - trackParams[1].MinPt[0] = 1.f / 12; // 7cl - - trackParams[2].MinTrackLength = 4; - trackParams[2].MinPt[0] = 1.f / 12; // 7cl - trackParams[2].MinPt[1] = 1.f / 5; // 6cl - trackParams[2].MinPt[2] = 1.f / 1; // 5cl - trackParams[2].MinPt[3] = 1.f / 6; // 4cl - - trackParams[2].StartLayerMask = (1 << 6) + (1 << 3); - if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { - trackParams[3].MinTrackLength = 4; - trackParams[3].TrackletMinPt = 0.1f; - trackParams[3].CellDeltaTanLambdaSigma *= 4.; - trackParams[3].DeltaROF = 0; // UPC specific setting - } - for (size_t ip = 0; ip < trackParams.size(); ip++) { - auto& param = trackParams[ip]; - param.ZBins = 64; - param.PhiBins = 32; - param.CellsPerClusterLimit = 1.e3f; - param.TrackletsPerClusterLimit = 1.e3f; - // check if something was overridden via configurable params - if (ip < trackConf.MaxIter) { - if (trackConf.startLayerMask[ip] > 0) { - trackParams[2].StartLayerMask = trackConf.startLayerMask[ip]; - } - if (trackConf.minTrackLgtIter[ip] > 0) { - param.MinTrackLength = trackConf.minTrackLgtIter[ip]; - } - for (int ilg = trackConf.MaxTrackLength; ilg >= trackConf.MinTrackLength; ilg--) { - int lslot0 = (trackConf.MaxTrackLength - ilg), lslot = lslot0 + ip * (trackConf.MaxTrackLength - trackConf.MinTrackLength + 1); - if (trackConf.minPtIterLgt[lslot] > 0.) { - param.MinPt[lslot0] = trackConf.minPtIterLgt[lslot]; - } - } - } - } - LOGP(info, "Initializing tracker in async. phase reconstruction with {} passes for tracking and {}/{} for vertexing", trackParams.size(), o2::its::VertexerParamConfig::Instance().nIterations, vertParams.size()); - vertParams[1].phiCut = 0.015f; - vertParams[1].tanLambdaCut = 0.015f; - vertParams[1].vertPerRofThreshold = 0; - vertParams[1].deltaRof = 0; - } else if (mMode == TrackingMode::Sync) { - trackParams.resize(1); - trackParams[0].ZBins = 64; - trackParams[0].PhiBins = 32; - trackParams[0].MinTrackLength = 4; - LOGP(info, "Initializing tracker in sync. phase reconstruction with {} passes", trackParams.size()); - vertParams.resize(1); - } else if (mMode == TrackingMode::Cosmics) { - mCosmicsProcessing = true; - mRunVertexer = false; - trackParams.resize(1); - trackParams[0].MinTrackLength = 4; - trackParams[0].CellDeltaTanLambdaSigma *= 10; - trackParams[0].PhiBins = 4; - trackParams[0].ZBins = 16; - trackParams[0].PVres = 1.e5f; - trackParams[0].MaxChi2ClusterAttachment = 60.; - trackParams[0].MaxChi2NDF = 40.; - trackParams[0].TrackletsPerClusterLimit = 100.; - trackParams[0].CellsPerClusterLimit = 100.; - LOGP(info, "Initializing tracker in reconstruction for cosmics with {} passes", trackParams.size()); - - } else { - throw std::runtime_error(fmt::format("Unsupported ITS tracking mode {:s} ", asString(mMode))); + const auto& vertConf = o2::its::VertexerParamConfig::Instance(); + if (auto parmode = (TrackingMode::Type)trackConf.trackingMode; mMode == TrackingMode::Unset || (parmode != TrackingMode::Unset && mMode != parmode)) { + LOGP(info, "Tracking mode overwritten by configurable params from {} to {}", TrackingMode::toString(mMode), TrackingMode::toString(parmode)); + mMode = parmode; } + auto trackParams = TrackingMode::getTrackingParameters(mMode); + auto vertParams = TrackingMode::getVertexingParameters(mMode); + LOGP(info, "Initializing tracker in {} phase reconstruction with {} passes for tracking and {}/{} for vertexing", TrackingMode::toString(mMode), trackParams.size(), o2::its::VertexerParamConfig::Instance().nIterations, vertParams.size()); + mTracker->setParameters(trackParams); + mVertexer->setParameters(vertParams); - // TODO this imposes the same memory limits on each iteration - for (auto& p : vertParams) { - p.PrintMemory = vertConf.printMemory; - p.MaxMemory = vertConf.maxMemory; - p.DropTFUponFailure = vertConf.dropTFUponFailure; - } - for (auto& p : trackParams) { - p.PrintMemory = trackConf.printMemory; - p.MaxMemory = trackConf.maxMemory; - p.DropTFUponFailure = trackConf.dropTFUponFailure; + if (mMode == TrackingMode::Cosmics) { + mRunVertexer = false; + mCosmicsProcessing = true; + LOGP(info, "Cosmic mode enabled, will skip vertexing"); } - for (auto& params : trackParams) { - params.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; - } - // adjust pT settings to actual mag. field - for (size_t ip = 0; ip < trackParams.size(); ip++) { - auto& param = trackParams[ip]; - param.TrackletMinPt *= bFactorTracklets; - for (int ilg = trackConf.MaxTrackLength; ilg >= trackConf.MinTrackLength; ilg--) { - int lslot = trackConf.MaxTrackLength - ilg; - param.MinPt[lslot] *= bFactor; + // threading + if (trackConf.nThreads == vertConf.nThreads) { + bool clamped{false}; + int nThreads = trackConf.nThreads; + if (nThreads > 0) { + const int hw = std::thread::hardware_concurrency(); + const int maxThreads = (hw == 0 ? 1 : hw); + nThreads = std::clamp(nThreads, 1, maxThreads); + clamped = trackConf.nThreads > maxThreads; } + LOGP(info, "Tracker and Vertexer will share the task arena with {} thread(s){}", nThreads, (clamped) ? " (clamped)" : ""); + mTaskArena = std::make_shared(std::abs(nThreads)); } - mTracker->setParameters(trackParams); - mVertexer->setParameters(vertParams); + mVertexer->setNThreads(vertConf.nThreads, mTaskArena); + mTracker->setNThreads(trackConf.nThreads, mTaskArena); } void ITSTrackingInterface::run(framework::ProcessingContext& pc) @@ -178,7 +99,7 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) irFrames.reserve(trackROFvec.size()); int nBCPerTF = alpParams.roFrameLengthInBC; - LOGP(info, "ITSTracker pulled {} clusters, {} RO frames", compClusters.size(), trackROFvec.size()); + LOGP(info, "ITSTracker pulled {} clusters, {} RO frames {}", compClusters.size(), trackROFvec.size(), compClusters.empty() ? " -> received no processable data will skip" : ""); const dataformats::MCTruthContainer* labels = nullptr; gsl::span mc2rofs; if (mIsMC) { @@ -198,6 +119,8 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) static pmr::vector dummyMCPurVerts; auto& allTrackLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "TRACKSMCTR", 0}) : dummyMCLabTracks; auto& allVerticesLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCTR", 0}) : dummyMCLabVerts; + bool writeContLabels = mIsMC && o2::its::VertexerParamConfig::Instance().outputContLabels; + auto& allVerticesContLabels = writeContLabels ? pc.outputs().make>(Output{"ITS", "VERTICESMCTRCONT", 0}) : dummyMCLabVerts; auto& allVerticesPurities = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCPUR", 0}) : dummyMCPurVerts; std::uint32_t roFrame = 0; @@ -216,11 +139,10 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) mTracker->setBz(o2::base::Propagator::Instance()->getNominalBz()); gsl::span::iterator pattIt = patterns.begin(); - - gsl::span trackROFspan(trackROFvec); + gsl::span trackROFspan(trackROFvec); loadROF(trackROFspan, compClusters, pattIt, labels); pattIt = patterns.begin(); - std::vector savedROF; + auto logger = [&](const std::string& s) { LOG(info) << s; }; auto fatalLogger = [&](const std::string& s) { LOG(fatal) << s; }; auto errorLogger = [&](const std::string& s) { LOG(error) << s; }; @@ -234,20 +156,26 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) if (mRunVertexer) { vertROFvec.reserve(trackROFvec.size()); // Run seeding vertexer - vertexerElapsedTime = mVertexer->clustersToVertices(logger); + if (!compClusters.empty()) { + vertexerElapsedTime = mVertexer->clustersToVertices(logger); + } } else { // cosmics mTimeFrame->resetRofPV(); } const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts gsl::span> vMCRecInfo; + gsl::span vMCContLabels; for (auto iRof{0}; iRof < trackROFspan.size(); ++iRof) { - std::vector vtxVecLoc; + bounded_vector vtxVecLoc; auto& vtxROF = vertROFvec.emplace_back(trackROFspan[iRof]); vtxROF.setFirstEntry(vertices.size()); if (mRunVertexer) { auto vtxSpan = mTimeFrame->getPrimaryVertices(iRof); if (mIsMC) { vMCRecInfo = mTimeFrame->getPrimaryVerticesMCRecInfo(iRof); + if (o2::its::VertexerParamConfig::Instance().outputContLabels) { + vMCContLabels = mTimeFrame->getPrimaryVerticesContributors(iRof); + } } if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { if (!vtxSpan.empty()) { @@ -267,17 +195,22 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } vtxROF.setNEntries(vtxSpan.size()); bool selROF = vtxSpan.empty(); - for (auto iV{0}; iV < vtxSpan.size(); ++iV) { - auto& v = vtxSpan[iV]; + for (int iV{0}, iVC{0}; iV < vtxSpan.size(); ++iV) { + const auto& v = vtxSpan[iV]; if (multEstConf.isVtxMultCutRequested() && !multEstConf.isPassingVtxMultCut(v.getNContributors())) { + iVC += v.getNContributors(); continue; // skip vertex of unwanted multiplicity } selROF = true; vertices.push_back(v); - if (mIsMC) { + if (mIsMC && !VertexerParamConfig::Instance().useTruthSeeding) { allVerticesLabels.push_back(vMCRecInfo[iV].first); allVerticesPurities.push_back(vMCRecInfo[iV].second); + if (o2::its::VertexerParamConfig::Instance().outputContLabels) { + allVerticesContLabels.insert(allVerticesContLabels.end(), vMCContLabels.begin() + iVC, vMCContLabels.begin() + iVC + v.getNContributors()); + } } + iVC += v.getNContributors(); } if (processingMask[iRof] && !selROF) { // passed selection in clusters and not in vertex multiplicity LOGP(info, "ROF {} rejected by the vertex multiplicity selection [{},{}]", iRof, multEstConf.cutMultVtxLow, multEstConf.cutMultVtxHigh); @@ -291,10 +224,10 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) for (auto& v : vtxVecLoc) { vertices.push_back(v); } - mTimeFrame->addPrimaryVertices(vtxVecLoc, iRof, 0); + mTimeFrame->addPrimaryVertices(vtxVecLoc, 0); } } - if (mRunVertexer) { + if (mRunVertexer && !compClusters.empty()) { LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} ({} + {}) vertices found in {}/{} ROFs", vertexerElapsedTime, mTimeFrame->getPrimaryVerticesNum(), @@ -312,14 +245,15 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) if (mCosmicsProcessing && compClusters.size() > 1500 * trackROFspan.size()) { LOG(error) << "Cosmics processing was requested with an average detector occupancy exceeding 1.e-7, skipping TF processing."; } else { - - mTimeFrame->setMultiplicityCutMask(processingMask); - mTimeFrame->setROFMask(processUPCMask); - // Run CA tracker - if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { - mTracker->clustersToTracks(logger, fatalLogger); - } else { - mTracker->clustersToTracks(logger, errorLogger); + if (!compClusters.empty()) { + mTimeFrame->setMultiplicityCutMask(processingMask); + mTimeFrame->setROFMask(processUPCMask); + // Run CA tracker + if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { + mTracker->clustersToTracks(logger, fatalLogger); + } else { + mTracker->clustersToTracks(logger, errorLogger); + } } size_t totTracks{mTimeFrame->getNumberOfTracks()}, totClusIDs{mTimeFrame->getNumberOfUsedClusters()}; if (totTracks) { @@ -362,14 +296,23 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) allTracks.emplace_back(trc); } } + } else { + for (auto& r : trackROFvec) { // reset data copied from the clusters + r.setFirstEntry(0); + r.setNEntries(0); + } } LOGP(info, "ITSTracker pushed {} tracks and {} vertices", allTracks.size(), vertices.size()); if (mIsMC) { LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); + if (!allVerticesContLabels.empty()) { + LOGP(info, "ITSTracker pushed {} vertex contributor labels", allVerticesContLabels.size()); + } LOGP(info, "ITSTracker pushed {} vertex purities", allVerticesPurities.size()); } } + mTimeFrame->wipe(); } void ITSTrackingInterface::updateTimeDependentParams(framework::ProcessingContext& pc) @@ -389,7 +332,6 @@ void ITSTrackingInterface::updateTimeDependentParams(framework::ProcessingContex GeometryTGeo* geom = GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); initialise(); - getConfiguration(pc); if (pc.services().get().inputTimesliceId == 0) { // print settings only for the 1st pipeling o2::its::VertexerParamConfig::Instance().printKeyValues(); @@ -408,12 +350,6 @@ void ITSTrackingInterface::updateTimeDependentParams(framework::ProcessingContex } } -void ITSTrackingInterface::getConfiguration(framework::ProcessingContext& pc) -{ - mVertexer->getGlobalConfiguration(); - mTracker->getGlobalConfiguration(); -} - void ITSTrackingInterface::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { @@ -445,21 +381,15 @@ void ITSTrackingInterface::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) void ITSTrackingInterface::printSummary() const { - mMemoryPool->print(); mTracker->printSummary(); } -void ITSTrackingInterface::end() -{ - mTimeFrame->wipe(); -} - -void ITSTrackingInterface::setTraitsFromProvider(VertexerTraits* vertexerTraits, - TrackerTraits7* trackerTraits, - TimeFrame7* frame) +void ITSTrackingInterface::setTraitsFromProvider(VertexerTraitsN* vertexerTraits, + TrackerTraitsN* trackerTraits, + TimeFrameN* frame) { - mVertexer = std::make_unique(vertexerTraits); - mTracker = std::make_unique(trackerTraits); + mVertexer = std::make_unique(vertexerTraits); + mTracker = std::make_unique(trackerTraits); mTimeFrame = frame; mVertexer->adoptTimeFrame(*mTimeFrame); mTracker->adoptTimeFrame(*mTimeFrame); @@ -475,7 +405,7 @@ void ITSTrackingInterface::setTraitsFromProvider(VertexerTraits* vertexerTraits, mVertexer->setMemoryPool(mMemoryPool); } -void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, +void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels) diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index a1a1a90da8963..c4b1fb427513f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -16,8 +16,9 @@ #include "ITStracking/Vertexer.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cluster.h" -#include "ITStracking/ROframe.h" + #include "ITStracking/ClusterLines.h" +#include "ITStracking/Tracklet.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/VertexerTraits.h" #include "ITStracking/TrackingConfigParam.h" @@ -25,7 +26,8 @@ namespace o2::its { -Vertexer::Vertexer(VertexerTraits* traits) : mTraits(traits) +template +Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) { if (!mTraits) { LOG(fatal) << "nullptr passed to ITS vertexer construction."; @@ -33,9 +35,15 @@ Vertexer::Vertexer(VertexerTraits* traits) : mTraits(traits) mVertParams.resize(1); } -float Vertexer::clustersToVertices(LogFunc logger) +template +float Vertexer::clustersToVertices(LogFunc logger) { LogFunc evalLog = [](const std::string&) {}; + + if (mTimeFrame->hasMCinformation() && mVertParams[0].useTruthSeeding) { + return evaluateTask(&Vertexer::addTruthSeeds, StateNames[mCurState = TruthSeeding], 0, evalLog); + } + TrackingParameters trkPars; TimeFrameGPUParameters tfGPUpar; mTraits->updateVertexingParameters(mVertParams, tfGPUpar); @@ -46,7 +54,6 @@ float Vertexer::clustersToVertices(LogFunc logger) throw err; } else { LOGP(error, "Dropping this TF!"); - mTimeFrame->resetTracklets(); } }; @@ -58,14 +65,11 @@ float Vertexer::clustersToVertices(LogFunc logger) logger(fmt::format("=== ITS {} Seeding vertexer iteration {} summary:", mTraits->getName(), iteration)); trkPars.PhiBins = mTraits->getVertexingParameters()[0].PhiBins; trkPars.ZBins = mTraits->getVertexingParameters()[0].ZBins; - auto timeInitIteration = evaluateTask( - &Vertexer::initialiseVertexer, StateNames[mCurState = Init], iteration, evalLog, trkPars, iteration); - auto timeTrackletIteration = evaluateTask( - &Vertexer::findTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration); + auto timeInitIteration = evaluateTask(&Vertexer::initialiseVertexer, StateNames[mCurState = Init], iteration, evalLog, trkPars, iteration); + auto timeTrackletIteration = evaluateTask(&Vertexer::findTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration); nTracklets01 = mTimeFrame->getTotalTrackletsTF(0); nTracklets12 = mTimeFrame->getTotalTrackletsTF(1); - auto timeSelectionIteration = evaluateTask( - &Vertexer::validateTracklets, StateNames[mCurState = Validating], iteration, evalLog, iteration); + auto timeSelectionIteration = evaluateTask(&Vertexer::validateTracklets, StateNames[mCurState = Validating], iteration, evalLog, iteration); auto timeVertexingIteration = evaluateTask(&Vertexer::findVertices, StateNames[mCurState = Finding], iteration, evalLog, iteration); printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration()[iteration], timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; @@ -84,46 +88,18 @@ float Vertexer::clustersToVertices(LogFunc logger) return timeInit + timeTracklet + timeSelection + timeVertexing; } -void Vertexer::getGlobalConfiguration() -{ - auto& vc = o2::its::VertexerParamConfig::Instance(); - auto& grc = o2::its::ITSGpuTrackingParamConfig::Instance(); - - // This is odd: we override only the parameters for the first iteration. - // Variations for the next iterations are set in the trackingInterfrace. - mVertParams[0].nIterations = vc.nIterations; - mVertParams[0].deltaRof = vc.deltaRof; - mVertParams[0].allowSingleContribClusters = vc.allowSingleContribClusters; - mVertParams[0].zCut = vc.zCut; - mVertParams[0].phiCut = vc.phiCut; - mVertParams[0].pairCut = vc.pairCut; - mVertParams[0].clusterCut = vc.clusterCut; - mVertParams[0].histPairCut = vc.histPairCut; - mVertParams[0].tanLambdaCut = vc.tanLambdaCut; - mVertParams[0].lowMultBeamDistCut = vc.lowMultBeamDistCut; - mVertParams[0].vertNsigmaCut = vc.vertNsigmaCut; - mVertParams[0].vertRadiusSigma = vc.vertRadiusSigma; - mVertParams[0].trackletSigma = vc.trackletSigma; - mVertParams[0].maxZPositionAllowed = vc.maxZPositionAllowed; - mVertParams[0].clusterContributorsCut = vc.clusterContributorsCut; - mVertParams[0].maxTrackletsPerCluster = vc.maxTrackletsPerCluster; - mVertParams[0].phiSpan = vc.phiSpan; - mVertParams[0].nThreads = vc.nThreads; - mVertParams[0].ZBins = vc.ZBins; - mVertParams[0].PhiBins = vc.PhiBins; - mVertParams[0].SaveTimeBenchmarks = vc.saveTimeBenchmarks; -} - -void Vertexer::adoptTimeFrame(TimeFrame7& tf) +template +void Vertexer::adoptTimeFrame(TimeFrameN& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } -void Vertexer::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) +template +void Vertexer::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) { logger(fmt::format(" - {} Vertexer: found {} | {} tracklets in: {} ms", mTraits->getName(), trackletN01, trackletN12, trackletT)); logger(fmt::format(" - {} Vertexer: selected {} tracklets in: {} ms", mTraits->getName(), selectedN, selecT)); @@ -134,4 +110,6 @@ void Vertexer::printEpilog(LogFunc& logger, } } +template class Vertexer<7>; + } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 37b650c05bd61..0c4ecb0b12df1 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -10,29 +10,30 @@ // or submit itself to any jurisdiction. /// -#include -#include -#include +#include +#include +#include +#include #include #include +#include #include "ITStracking/VertexerTraits.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Tracklet.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "Steer/MCKinematicsReader.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "DetectorsRaw/HBFUtils.h" +#include "CommonUtils/TreeStreamRedirector.h" -#ifdef VTX_DEBUG -#include "TTree.h" -#include "TFile.h" -#include -#include -#endif - -using namespace o2::its; +namespace o2::its +{ -template -void trackleterKernelHost( +template +static void trackleterKernelHost( const gsl::span& clustersNextLayer, // 0 2 const gsl::span& clustersCurrentLayer, // 1 1 const gsl::span& usedClustersNextLayer, // 0 2 @@ -40,7 +41,7 @@ void trackleterKernelHost( const float phiCut, bounded_vector& tracklets, gsl::span foundTracklets, - const IndexTableUtils& utils, + const IndexTableUtils& utils, const short pivotRof, const short targetRof, gsl::span rofFoundTrackletsOffsets, // we want to change those, to keep track of the offset in deltaRof>0 @@ -52,7 +53,7 @@ void trackleterKernelHost( for (int iCurrentLayerClusterIndex = 0; iCurrentLayerClusterIndex < clustersCurrentLayer.size(); ++iCurrentLayerClusterIndex) { int storedTracklets{0}; const Cluster& currentCluster{clustersCurrentLayer[iCurrentLayerClusterIndex]}; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, utils)}; + 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) { @@ -92,21 +93,22 @@ void trackleterKernelHost( } } -void trackletSelectionKernelHost( +static void trackletSelectionKernelHost( const gsl::span clusters0, // 0 const gsl::span clusters1, // 1 gsl::span usedClusters0, // Layer 0 gsl::span usedClusters2, // Layer 2 const gsl::span& tracklets01, const gsl::span& tracklets12, - bounded_vector& usedTracklets, + bounded_vector& usedTracklets, const gsl::span foundTracklets01, const gsl::span foundTracklets12, bounded_vector& lines, const gsl::span& trackletLabels, bounded_vector& linesLabels, - const short pivotRofId, - const short targetRofId, + const short targetRofId0, + const short targetRofId2, + bool safeWrites = false, const float tanLambdaCut = 0.025f, const float phiCut = 0.005f, const int maxTracklets = static_cast(1e2)) @@ -116,16 +118,27 @@ void trackletSelectionKernelHost( int validTracklets{0}; for (int iTracklet12{offset12}; iTracklet12 < offset12 + foundTracklets12[iCurrentLayerClusterIndex]; ++iTracklet12) { for (int iTracklet01{offset01}; iTracklet01 < offset01 + foundTracklets01[iCurrentLayerClusterIndex]; ++iTracklet01) { + if (usedTracklets[iTracklet01]) { + continue; + } + const auto& tracklet01{tracklets01[iTracklet01]}; const auto& tracklet12{tracklets12[iTracklet12]}; - if (tracklet01.rof[0] != targetRofId || tracklet12.rof[1] != targetRofId) { + + if (tracklet01.rof[0] != targetRofId0 || tracklet12.rof[1] != targetRofId2) { 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 (!usedTracklets[iTracklet01] && deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTracklets) { - usedClusters0[tracklet01.firstClusterIndex] = true; - usedClusters2[tracklet12.secondClusterIndex] = true; + if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTracklets) { + if (safeWrites) { + __atomic_store_n(&usedClusters0[tracklet01.firstClusterIndex], 1, __ATOMIC_RELAXED); + __atomic_store_n(&usedClusters2[tracklet12.secondClusterIndex], 1, __ATOMIC_RELAXED); + } else { + usedClusters0[tracklet01.firstClusterIndex] = 1; + usedClusters2[tracklet12.secondClusterIndex] = 1; + } usedTracklets[iTracklet01] = true; lines.emplace_back(tracklet01, clusters0.data(), clusters1.data()); if (!trackletLabels.empty()) { @@ -140,27 +153,8 @@ void trackletSelectionKernelHost( } } -bounded_vector> VertexerTraits::selectClusters(const int* indexTable, - const std::array& selectedBinsRect, - const IndexTableUtils& utils) -{ - bounded_vector> filteredBins{mMemoryPool.get()}; - int phiBinsNum{selectedBinsRect[3] - selectedBinsRect[1] + 1}; - if (phiBinsNum < 0) { - phiBinsNum += utils.getNphiBins(); - } - filteredBins.reserve(phiBinsNum); - for (int iPhiBin{selectedBinsRect[1]}, iPhiCount{0}; iPhiCount < phiBinsNum; - iPhiBin = ++iPhiBin == utils.getNphiBins() ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{utils.getBinIndex(selectedBinsRect[0], iPhiBin)}; - filteredBins.emplace_back( - indexTable[firstBinIndex], - utils.countRowSelectedBins(indexTable, iPhiBin, selectedBinsRect[0], selectedBinsRect[2])); - } - return filteredBins; -} - -void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) +template +void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) { mVrtParams = vrtPar; mIndexTableUtils.setTrackingParameters(vrtPar[0]); @@ -168,168 +162,128 @@ void VertexerTraits::updateVertexingParameters(const std::vector(std::ceil(mIndexTableUtils.getNphiBins() * par.phiCut / o2::constants::math::TwoPI)); par.zSpan = static_cast(std::ceil(par.zCut * mIndexTableUtils.getInverseZCoordinate(0))); } - setNThreads(vrtPar[0].nThreads); } // Main functions -void VertexerTraits::computeTracklets(const int iteration) +template +void VertexerTraits::computeTracklets(const int iteration) { - mTaskArena.execute([&] { - tbb::parallel_for( - tbb::blocked_range(0, (short)mTimeFrame->getNrof()), - [&](const tbb::blocked_range& Rofs) { - for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) { - bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { - trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof - mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof - mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof - mVrtParams[iteration].phiCut, - mTimeFrame->getTracklets()[0], // Flat tracklet buffer - mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof - mIndexTableUtils, - pivotRofId, - targetRofId, - gsl::span(), // Offset in the tracklet buffer - mVrtParams[iteration].maxTrackletsPerCluster); - trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), - mTimeFrame->getUsedClustersROF(targetRofId, 2), - mTimeFrame->getIndexTable(targetRofId, 2).data(), - mVrtParams[iteration].phiCut, - mTimeFrame->getTracklets()[1], - mTimeFrame->getNTrackletsCluster(pivotRofId, 1), // Span of the number of tracklets per each cluster in pivot rof - mIndexTableUtils, - pivotRofId, - targetRofId, - gsl::span(), // Offset in the tracklet buffer - mVrtParams[iteration].maxTrackletsPerCluster); - } - mTimeFrame->getNTrackletsROF(pivotRofId, 0) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 0).end(), 0); - mTimeFrame->getNTrackletsROF(pivotRofId, 1) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 1).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 1).end(), 0); - } - }); - }); + mTaskArena->execute([&] { + tbb::parallel_for(0, mTimeFrame->getNrof(), [&](const short pivotRofId) { + bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; + short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; + short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; + for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { + trackleterKernelHost( + !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof + !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof + mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof + mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof + mVrtParams[iteration].phiCut, + mTimeFrame->getTracklets()[0], // Flat tracklet buffer + mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof + mIndexTableUtils, + pivotRofId, + targetRofId, + gsl::span(), // Offset in the tracklet buffer + mVrtParams[iteration].maxTrackletsPerCluster); + trackleterKernelHost( + !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), + !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + mTimeFrame->getUsedClustersROF(targetRofId, 2), + mTimeFrame->getIndexTable(targetRofId, 2).data(), + mVrtParams[iteration].phiCut, + mTimeFrame->getTracklets()[1], + mTimeFrame->getNTrackletsCluster(pivotRofId, 1), // Span of the number of tracklets per each cluster in pivot rof + mIndexTableUtils, + pivotRofId, + targetRofId, + gsl::span(), // Offset in the tracklet buffer + mVrtParams[iteration].maxTrackletsPerCluster); + } + mTimeFrame->getNTrackletsROF(pivotRofId, 0) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 0).end(), 0); + mTimeFrame->getNTrackletsROF(pivotRofId, 1) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 1).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 1).end(), 0); + }); - mTimeFrame->computeTrackletsPerROFScans(); - mTimeFrame->getTracklets()[0].resize(mTimeFrame->getTotalTrackletsTF(0)); - mTimeFrame->getTracklets()[1].resize(mTimeFrame->getTotalTrackletsTF(1)); + mTimeFrame->computeTrackletsPerROFScans(); + if (auto tot0 = mTimeFrame->getTotalTrackletsTF(0), tot1 = mTimeFrame->getTotalTrackletsTF(1); + tot0 == 0 || tot1 == 0) { + return; + } else { + mTimeFrame->getTracklets()[0].resize(tot0); + mTimeFrame->getTracklets()[1].resize(tot1); + } - mTaskArena.execute([&] { - tbb::parallel_for( - tbb::blocked_range(0, (short)mTimeFrame->getNrof()), - [&](const tbb::blocked_range& Rofs) { - for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) { - bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - auto mobileOffset0 = mTimeFrame->getNTrackletsROF(pivotRofId, 0); - auto mobileOffset1 = mTimeFrame->getNTrackletsROF(pivotRofId, 1); - for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { - trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), - mTimeFrame->getUsedClustersROF(targetRofId, 0), - mTimeFrame->getIndexTable(targetRofId, 0).data(), - mVrtParams[iteration].phiCut, - mTimeFrame->getTracklets()[0], - mTimeFrame->getNTrackletsCluster(pivotRofId, 0), - mIndexTableUtils, - pivotRofId, - targetRofId, - mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 0), - mVrtParams[iteration].maxTrackletsPerCluster); - trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), - mTimeFrame->getUsedClustersROF(targetRofId, 2), - mTimeFrame->getIndexTable(targetRofId, 2).data(), - mVrtParams[iteration].phiCut, - mTimeFrame->getTracklets()[1], - mTimeFrame->getNTrackletsCluster(pivotRofId, 1), - mIndexTableUtils, - pivotRofId, - targetRofId, - mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 1), - mVrtParams[iteration].maxTrackletsPerCluster); - } - } - }); + tbb::parallel_for(0, mTimeFrame->getNrof(), [&](const short pivotRofId) { + bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; + short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; + short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; + auto mobileOffset0 = mTimeFrame->getNTrackletsROF(pivotRofId, 0); + auto mobileOffset1 = mTimeFrame->getNTrackletsROF(pivotRofId, 1); + for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { + trackleterKernelHost( + !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), + !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + mTimeFrame->getUsedClustersROF(targetRofId, 0), + mTimeFrame->getIndexTable(targetRofId, 0).data(), + mVrtParams[iteration].phiCut, + mTimeFrame->getTracklets()[0], + mTimeFrame->getNTrackletsCluster(pivotRofId, 0), + mIndexTableUtils, + pivotRofId, + targetRofId, + mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 0), + mVrtParams[iteration].maxTrackletsPerCluster); + trackleterKernelHost( + !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), + !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + mTimeFrame->getUsedClustersROF(targetRofId, 2), + mTimeFrame->getIndexTable(targetRofId, 2).data(), + mVrtParams[iteration].phiCut, + mTimeFrame->getTracklets()[1], + mTimeFrame->getNTrackletsCluster(pivotRofId, 1), + mIndexTableUtils, + pivotRofId, + targetRofId, + mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 1), + mVrtParams[iteration].maxTrackletsPerCluster); + } + }); }); /// Create tracklets labels for L0-L1, information is as flat as in tracklets vector (no rofId) if (mTimeFrame->hasMCinformation()) { for (const auto& trk : mTimeFrame->getTracklets()[0]) { o2::MCCompLabel label; - int sortedId0{mTimeFrame->getSortedIndex(trk.rof[0], 0, trk.firstClusterIndex)}; - int sortedId1{mTimeFrame->getSortedIndex(trk.rof[1], 1, trk.secondClusterIndex)}; - for (const auto& lab0 : mTimeFrame->getClusterLabels(0, mTimeFrame->getClusters()[0][sortedId0].clusterId)) { - for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { - if (lab0 == lab1 && lab0.isValid()) { - label = lab0; + if (!trk.isEmpty()) { + int sortedId0{mTimeFrame->getSortedIndex(trk.rof[0], 0, trk.firstClusterIndex)}; + int sortedId1{mTimeFrame->getSortedIndex(trk.rof[1], 1, trk.secondClusterIndex)}; + for (const auto& lab0 : mTimeFrame->getClusterLabels(0, mTimeFrame->getClusters()[0][sortedId0].clusterId)) { + for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { + if (lab0 == lab1 && lab0.isValid()) { + label = lab0; + break; + } + } + if (label.isValid()) { break; } } - if (label.isValid()) { - break; - } } mTimeFrame->getTrackletsLabel(0).emplace_back(label); } } #ifdef VTX_DEBUG - // Dump on file - TFile* trackletFile = TFile::Open("artefacts_tf.root", "recreate"); - TTree* tr_tre = new TTree("tracklets", "tf"); - std::vector trkl_vec_0(0); - std::vector trkl_vec_1(0); - std::vector clus0(0); - std::vector clus1(0); - std::vector clus2(0); - tr_tre->Branch("Tracklets0", &trkl_vec_0); - tr_tre->Branch("Tracklets1", &trkl_vec_1); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - trkl_vec_0.clear(); - trkl_vec_1.clear(); - for (auto& tr : mTimeFrame->getFoundTracklets(rofId, 0)) { - trkl_vec_0.push_back(tr); - } - for (auto& tr : mTimeFrame->getFoundTracklets(rofId, 1)) { - trkl_vec_1.push_back(tr); - } - tr_tre->Fill(); - } - trackletFile->cd(); - tr_tre->Write(); - trackletFile->Close(); - - std::ofstream out01("NTC01_cpu.txt"), out12("NTC12_cpu.txt"); - for (int iRof{0}; iRof < mTimeFrame->getNrof(); ++iRof) { - out01 << "ROF: " << iRof << std::endl; - out12 << "ROF: " << iRof << std::endl; - std::copy(mTimeFrame->getNTrackletsCluster(iRof, 0).begin(), mTimeFrame->getNTrackletsCluster(iRof, 0).end(), std::ostream_iterator(out01, "\t")); - out01 << std::endl; - std::copy(mTimeFrame->getExclusiveNTrackletsCluster(iRof, 0).begin(), mTimeFrame->getExclusiveNTrackletsCluster(iRof, 0).end(), std::ostream_iterator(out01, "\t")); - std::copy(mTimeFrame->getNTrackletsCluster(iRof, 1).begin(), mTimeFrame->getNTrackletsCluster(iRof, 1).end(), std::ostream_iterator(out12, "\t")); - out12 << std::endl; - std::copy(mTimeFrame->getExclusiveNTrackletsCluster(iRof, 1).begin(), mTimeFrame->getExclusiveNTrackletsCluster(iRof, 1).end(), std::ostream_iterator(out12, "\t")); - out01 << std::endl; - out12 << std::endl; - } - out01.close(); - out12.close(); + debugComputeTracklets(iteration); #endif } -void VertexerTraits::computeTrackletMatching(const int iteration) +template +void VertexerTraits::computeTrackletMatching(const int iteration) { - mTaskArena.execute([&] { + mTaskArena->execute([&] { + tbb::combinable totalLines{0}; tbb::parallel_for( tbb::blocked_range(0, (short)mTimeFrame->getNrof()), [&](const tbb::blocked_range& Rofs) { @@ -337,72 +291,63 @@ void VertexerTraits::computeTrackletMatching(const int iteration) if (iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold) { continue; } + if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty()) { + continue; + } mTimeFrame->getLines(pivotRofId).reserve(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).size()); - bounded_vector usedTracklets(mTimeFrame->getFoundTracklets(pivotRofId, 0).size(), false, mMemoryPool.get()); + bounded_vector usedTracklets(mTimeFrame->getFoundTracklets(pivotRofId, 0).size(), false, mMemoryPool.get()); short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - for (short targetRofId = startROF; targetRofId < endROF; ++targetRofId) { - trackletSelectionKernelHost( - mTimeFrame->getClustersOnLayer(targetRofId, 0), - mTimeFrame->getClustersOnLayer(pivotRofId, 1), - mTimeFrame->getUsedClustersROF(targetRofId, 0), - mTimeFrame->getUsedClustersROF(targetRofId, 2), - mTimeFrame->getFoundTracklets(pivotRofId, 0), - mTimeFrame->getFoundTracklets(pivotRofId, 1), - usedTracklets, - mTimeFrame->getNTrackletsCluster(pivotRofId, 0), - mTimeFrame->getNTrackletsCluster(pivotRofId, 1), - mTimeFrame->getLines(pivotRofId), - mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0), - mTimeFrame->getLinesLabel(pivotRofId), - pivotRofId, - targetRofId, - mVrtParams[iteration].tanLambdaCut, - mVrtParams[iteration].phiCut); + + // needed only if multi-threaded using deltaRof and only at the overlap edges of the ranges + bool safeWrite = mTaskArena->max_concurrency() > 1 && mVrtParams[iteration].deltaRof != 0 && ((Rofs.begin() - startROF < 0) || (endROF - Rofs.end() > 0)); + + for (short targetRofId0 = startROF; targetRofId0 < endROF; ++targetRofId0) { + for (short targetRofId2 = startROF; targetRofId2 < endROF; ++targetRofId2) { + if (std::abs(targetRofId0 - targetRofId2) > mVrtParams[iteration].deltaRof) { // do not allow over 3 ROFs + continue; + } + trackletSelectionKernelHost( + mTimeFrame->getClustersOnLayer(targetRofId0, 0), + mTimeFrame->getClustersOnLayer(pivotRofId, 1), + mTimeFrame->getUsedClustersROF(targetRofId0, 0), + mTimeFrame->getUsedClustersROF(targetRofId2, 2), + mTimeFrame->getFoundTracklets(pivotRofId, 0), + mTimeFrame->getFoundTracklets(pivotRofId, 1), + usedTracklets, + mTimeFrame->getNTrackletsCluster(pivotRofId, 0), + mTimeFrame->getNTrackletsCluster(pivotRofId, 1), + mTimeFrame->getLines(pivotRofId), + mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0), + mTimeFrame->getLinesLabel(pivotRofId), + targetRofId0, + targetRofId2, + safeWrite, + mVrtParams[iteration].tanLambdaCut, + mVrtParams[iteration].phiCut); + } } + totalLines.local() += mTimeFrame->getLines(pivotRofId).size(); } }); + mTimeFrame->setNLinesTotal(totalLines.combine(std::plus())); }); #ifdef VTX_DEBUG - TFile* trackletFile = TFile::Open("artefacts_tf.root", "update"); - TTree* ln_tre = new TTree("lines", "tf"); - std::vector lines_vec(0); - std::vector nTrackl01(0); - std::vector nTrackl12(0); - ln_tre->Branch("Lines", &lines_vec); - ln_tre->Branch("NTrackletCluster01", &nTrackl01); - ln_tre->Branch("NTrackletCluster12", &nTrackl12); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - lines_vec.clear(); - nTrackl01.clear(); - nTrackl12.clear(); - for (auto& ln : mTimeFrame->getLines(rofId)) { - lines_vec.push_back(ln); - } - for (auto& n : mTimeFrame->getNTrackletsCluster(rofId, 0)) { - nTrackl01.push_back(n); - } - for (auto& n : mTimeFrame->getNTrackletsCluster(rofId, 1)) { - nTrackl12.push_back(n); - } - - ln_tre->Fill(); - } - trackletFile->cd(); - ln_tre->Write(); - trackletFile->Close(); + debugComputeTrackletMatching(iteration); #endif + + // from here on we do not use tracklets from L1-2 anymore, so let's free them + deepVectorClear(mTimeFrame->getTracklets()[1]); } -void VertexerTraits::computeVertices(const int iteration) +template +void VertexerTraits::computeVertices(const int iteration) { auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; bounded_vector vertices(mMemoryPool.get()); bounded_vector> polls(mMemoryPool.get()); -#ifdef VTX_DEBUG - std::vector> dbg_clusLines(mTimeFrame->getNrof()); -#endif + bounded_vector contLabels(mMemoryPool.get()); bounded_vector noClustersVec(mTimeFrame->getNrof(), 0, mMemoryPool.get()); for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { if (iteration && (int)mTimeFrame->getPrimaryVertices(rofId).size() > mVrtParams[iteration].vertPerRofThreshold) { @@ -482,14 +427,8 @@ void VertexerTraits::computeVertices(const int iteration) } } for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - vertices.clear(); std::sort(mTimeFrame->getTrackletClusters(rofId).begin(), mTimeFrame->getTrackletClusters(rofId).end(), - [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); // ensure clusters are ordered by contributors, so that we can cat after the first. -#ifdef VTX_DEBUG - for (auto& cl : mTimeFrame->getTrackletClusters(rofId)) { - dbg_clusLines[rofId].push_back(cl); - } -#endif + [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); // ensure clusters are ordered by contributors, so that we can cat after the first. bool atLeastOneFound{false}; for (int iCluster{0}; iCluster < noClustersVec[rofId]; ++iCluster) { bool lowMultCandidate{false}; @@ -506,196 +445,399 @@ void VertexerTraits::computeVertices(const int iteration) if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { atLeastOneFound = true; - vertices.emplace_back(o2::math_utils::Point3D(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0], - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1], - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]), - mTimeFrame->getTrackletClusters(rofId)[iCluster].getRMS2(), // Symm matrix. Diagonal: RMS2 components, - // off-diagonal: square mean of projections on planes. - mTimeFrame->getTrackletClusters(rofId)[iCluster].getSize(), // Contributors - mTimeFrame->getTrackletClusters(rofId)[iCluster].getAvgDistance2()); // In place of chi2 + auto& vertex = vertices.emplace_back(o2::math_utils::Point3D(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0], + mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1], + mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]), + mTimeFrame->getTrackletClusters(rofId)[iCluster].getRMS2(), // Symm matrix. Diagonal: RMS2 components, + // off-diagonal: square mean of projections on planes. + mTimeFrame->getTrackletClusters(rofId)[iCluster].getSize(), // Contributors + mTimeFrame->getTrackletClusters(rofId)[iCluster].getAvgDistance2()); // In place of chi2 if (iteration) { - vertices.back().setFlags(Vertex::UPCMode); + vertex.setFlags(Vertex::UPCMode); } - vertices.back().setTimeStamp(mTimeFrame->getTrackletClusters(rofId)[iCluster].getROF()); + vertex.setTimeStamp(mTimeFrame->getTrackletClusters(rofId)[iCluster].getROF()); if (mTimeFrame->hasMCinformation()) { bounded_vector labels(mMemoryPool.get()); for (auto& index : mTimeFrame->getTrackletClusters(rofId)[iCluster].getLabels()) { labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); // then we can use nContributors from vertices to get the labels } polls.push_back(computeMain(labels)); + if (mVrtParams[iteration].outputContLabels) { + contLabels.insert(contLabels.end(), labels.begin(), labels.end()); + } } } } if (!iteration) { - mTimeFrame->addPrimaryVertices(vertices, rofId, iteration); + mTimeFrame->addPrimaryVertices(vertices, iteration); if (mTimeFrame->hasMCinformation()) { mTimeFrame->addPrimaryVerticesLabels(polls); + if (mVrtParams[iteration].outputContLabels) { + mTimeFrame->addPrimaryVerticesContributorLabels(contLabels); + } } } else { mTimeFrame->addPrimaryVerticesInROF(vertices, rofId, iteration); if (mTimeFrame->hasMCinformation()) { mTimeFrame->addPrimaryVerticesLabelsInROF(polls, rofId); + if (mVrtParams[iteration].outputContLabels) { + mTimeFrame->addPrimaryVerticesContributorLabelsInROF(contLabels, rofId); + } } } if (vertices.empty() && !(iteration && (int)mTimeFrame->getPrimaryVertices(rofId).size() > mVrtParams[iteration].vertPerRofThreshold)) { mTimeFrame->getNoVertexROF()++; } + vertices.clear(); + polls.clear(); } + #ifdef VTX_DEBUG - TFile* dbg_file = TFile::Open("artefacts_tf.root", "update"); - TTree* ln_clus_lines_tree = new TTree("clusterlines", "tf"); - std::vector cl_lines_vec_pre(0); - std::vector cl_lines_vec_post(0); - ln_clus_lines_tree->Branch("cllines_pre", &cl_lines_vec_pre); - ln_clus_lines_tree->Branch("cllines_post", &cl_lines_vec_post); - for (auto rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - cl_lines_vec_pre.clear(); - cl_lines_vec_post.clear(); - for (auto& clln : mTimeFrame->getTrackletClusters(rofId)) { - cl_lines_vec_post.push_back(clln); + debugComputeVertices(iteration); +#endif +} + +template +void VertexerTraits::addTruthSeedingVertices() +{ + LOGP(info, "Using truth seeds as vertices; will skip computations"); + mTimeFrame->resetRofPV(); + const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto irs = dc->getEventRecords(); + int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + o2::steer::MCKinematicsReader mcReader(dc); + struct VertInfo { + bounded_vector vertices; + bounded_vector srcs; + bounded_vector events; + }; + std::map vertices; + const int iSrc = 0; // take only events from collision generator + auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); + for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { + const auto& ir = irs[eveId2colId[iEve]]; + if (!ir.isDummy()) { // do we need this, is this for diffractive events? + const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); + int rofId = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) / roFrameLengthInBC; + if (!vertices.contains(rofId)) { + vertices[rofId] = { + .vertices = bounded_vector(mMemoryPool.get()), + .srcs = bounded_vector(mMemoryPool.get()), + .events = bounded_vector(mMemoryPool.get()), + }; + } + Vertex vert; + vert.setTimeStamp(rofId); + // set minimum to 1 sometimes for diffractive events there is nothing acceptance + vert.setNContributors(std::max(1L, std::ranges::count_if(mcReader.getTracks(iSrc, iEve), [](const auto& trk) { + return trk.isPrimary() && trk.GetPt() > 0.05 && std::abs(trk.GetEta()) < 1.1; + }))); + vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); + vert.setChi2(1); // not used as constraint + constexpr float cov = 50e-9; + vert.setCov(cov, cov, cov, cov, cov, cov); + vertices[rofId].vertices.push_back(vert); + vertices[rofId].srcs.push_back(iSrc); + vertices[rofId].events.push_back(iEve); } - for (auto& cl : dbg_clusLines[rofId]) { - cl_lines_vec_pre.push_back(cl); + mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); + } + size_t nVerts{0}; + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + bounded_vector verts(mMemoryPool.get()); + bounded_vector> polls(mMemoryPool.get()); + if (vertices.contains(iROF)) { + const auto& vertInfo = vertices[iROF]; + verts = vertInfo.vertices; + nVerts += verts.size(); + for (size_t i{0}; i < verts.size(); ++i) { + o2::MCCompLabel lbl(o2::MCCompLabel::maxTrackID(), vertInfo.events[i], vertInfo.srcs[i], false); + polls.emplace_back(lbl, 1.f); + } + } else { + mTimeFrame->getNoVertexROF()++; } - ln_clus_lines_tree->Fill(); + mTimeFrame->addPrimaryVertices(verts, 0); + mTimeFrame->addPrimaryVerticesLabels(polls); + } + LOGP(info, "Found {}/{} ROFs with {} vertices -> ={:.2f}", vertices.size(), mTimeFrame->getNrof(), nVerts, (float)nVerts / (float)vertices.size()); +} + +template +void VertexerTraits::setNThreads(int n, std::shared_ptr& arena) +{ +#if defined(VTX_DEBUG) + LOGP(info, "Vertexer with debug output forcing single thread"); + mTaskArena = std::make_shared(1); +#else + if (arena == nullptr) { + mTaskArena = std::make_shared(std::abs(n)); + LOGP(info, "Setting seeding vertexer with {} threads.", n); + } else { + mTaskArena = arena; + LOGP(info, "Attaching vertexer to calling thread's arena"); } - dbg_file->cd(); - ln_clus_lines_tree->Write(); - dbg_file->Close(); #endif } -void VertexerTraits::computeVerticesInRof(int rofId, - gsl::span& lines, - bounded_vector& usedLines, - bounded_vector& clusterLines, - std::array& beamPosXY, - bounded_vector& vertices, - bounded_vector& verticesInRof, - TimeFrame7* tf, - bounded_vector* labels, - const int iteration) +template +void VertexerTraits::debugComputeTracklets(int iteration) { - int foundVertices{0}; - auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; - const int numTracklets{static_cast(lines.size())}; - for (int line1{0}; line1 < numTracklets; ++line1) { - if (usedLines[line1]) { - continue; - } - for (int line2{line1 + 1}; line2 < numTracklets; ++line2) { - if (usedLines[line2]) { - continue; + auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "recreate"); + LOGP(info, "writing debug output for computeTracklets"); + for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + const auto& strk0 = mTimeFrame->getFoundTracklets(rofId, 0); + std::vector trk0(strk0.begin(), strk0.end()); + const auto& strk1 = mTimeFrame->getFoundTracklets(rofId, 1); + std::vector trk1(strk1.begin(), strk1.end()); + (*stream) << "tracklets" + << "Tracklets0=" << trk0 + << "Tracklets1=" << trk1 + << "iteration=" << iteration + << "\n"; + } + stream->Close(); + delete stream; +} + +template +void VertexerTraits::debugComputeTrackletMatching(int iteration) +{ + auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "update"); + LOGP(info, "writing debug output for computeTrackletMatching"); + for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + (*stream) << "lines" + << "Lines=" << toSTDVector(mTimeFrame->getLines(rofId)) + << "NTrackletCluster01=" << mTimeFrame->getNTrackletsCluster(rofId, 0) + << "NTrackletCluster12=" << mTimeFrame->getNTrackletsCluster(rofId, 1) + << "iteration=" << iteration + << "\n"; + } + + if (mTimeFrame->hasMCinformation()) { + LOGP(info, "\tdumping also MC information"); + const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto irs = dc->getEventRecords(); + int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + o2::steer::MCKinematicsReader mcReader(dc); + + std::map eve2BcInROF, bcInRofNEve; + for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { + auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); + for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { + const auto& ir = irs[eveId2colId[iEve]]; + if (!ir.isDummy()) { // do we need this, is this for diffractive events? + const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); + const int bcInROF = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) % roFrameLengthInBC; + eve2BcInROF[iEve] = bcInROF; + ++bcInRofNEve[bcInROF]; + } } - auto dca{Line::getDCA(lines[line1], lines[line2])}; - if (dca < mVrtParams[iteration].pairCut) { - clusterLines.emplace_back(line1, lines[line1], line2, lines[line2]); - std::array tmpVertex{clusterLines.back().getVertex()}; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { - clusterLines.pop_back(); - break; + } + + std::unordered_map bcROFNTracklets01, bcROFNTracklets12; + std::vector> tracklet01BC, tracklet12BC; + for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + { // 0-1 + const auto& tracklet01 = mTimeFrame->getFoundTracklets(rofId, 0); + const auto& lbls01 = mTimeFrame->getLabelsFoundTracklets(rofId, 0); + auto& trkls01 = tracklet01BC.emplace_back(); + for (int iTrklt{0}; iTrklt < (int)tracklet01.size(); ++iTrklt) { + const auto& tracklet = tracklet01[iTrklt]; + const auto& lbl = lbls01[iTrklt]; + if (lbl.isCorrect()) { + ++bcROFNTracklets01[eve2BcInROF[lbl.getEventID()]]; + trkls01.push_back(eve2BcInROF[lbl.getEventID()]); + } else { + trkls01.push_back(-1); + } } - usedLines[line1] = true; - usedLines[line2] = true; - for (int tracklet3{0}; tracklet3 < numTracklets; ++tracklet3) { - if (usedLines[tracklet3]) { - continue; + } + { // 1-2 computed on the fly! + const auto& tracklet12 = mTimeFrame->getFoundTracklets(rofId, 1); + auto& trkls12 = tracklet12BC.emplace_back(); + for (int iTrklt{0}; iTrklt < (int)tracklet12.size(); ++iTrklt) { + const auto& tracklet = tracklet12[iTrklt]; + o2::MCCompLabel label; + + int sortedId1{mTimeFrame->getSortedIndex(tracklet.rof[0], 1, tracklet.firstClusterIndex)}; + int sortedId2{mTimeFrame->getSortedIndex(tracklet.rof[1], 2, tracklet.secondClusterIndex)}; + for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { + for (const auto& lab2 : mTimeFrame->getClusterLabels(2, mTimeFrame->getClusters()[2][sortedId2].clusterId)) { + if (lab1 == lab2 && lab1.isValid()) { + label = lab1; + break; + } + } + if (label.isValid()) { + break; + } } - if (Line::getDistanceFromPoint(lines[tracklet3], tmpVertex) < mVrtParams[iteration].pairCut) { - clusterLines.back().add(tracklet3, lines[tracklet3]); - usedLines[tracklet3] = true; - tmpVertex = clusterLines.back().getVertex(); + + if (label.isCorrect()) { + ++bcROFNTracklets12[eve2BcInROF[label.getEventID()]]; + trkls12.push_back(eve2BcInROF[label.getEventID()]); + } else { + trkls12.push_back(-1); } } - break; } } - } + LOGP(info, "\tdumping ntracklets/RofBC ({})", bcInRofNEve.size()); + for (const auto& [bcInRof, neve] : bcInRofNEve) { + (*stream) << "ntracklets" + << "bcInROF=" << bcInRof + << "ntrkl01=" << bcROFNTracklets01[bcInRof] + << "ntrkl12=" << bcROFNTracklets12[bcInRof] + << "neve=" << neve + << "iteration=" << iteration + << "\n"; + } - if (mVrtParams[iteration].allowSingleContribClusters) { - auto beamLine = Line{{tf->getBeamX(), tf->getBeamY(), -50.f}, {tf->getBeamX(), tf->getBeamY(), 50.f}}; // use beam position as contributor - for (size_t iLine{0}; iLine < numTracklets; ++iLine) { - if (!usedLines[iLine]) { - auto dca = Line::getDCA(lines[iLine], beamLine); - if (dca < mVrtParams[iteration].pairCut) { - clusterLines.emplace_back(iLine, lines[iLine], -1, beamLine); // beamline must be passed as second line argument + std::unordered_map bcROFNLines; + for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + const auto& lines = mTimeFrame->getLines(rofId); + const auto& lbls = mTimeFrame->getLinesLabel(rofId); + for (int iLine{0}; iLine < (int)lines.size(); ++iLine) { + const auto& line = lines[iLine]; + const auto& lbl = lbls[iLine]; + if (lbl.isCorrect()) { + ++bcROFNLines[eve2BcInROF[lbl.getEventID()]]; } } } + + LOGP(info, "\tdumping nlines/RofBC"); + for (const auto& [bcInRof, neve] : bcInRofNEve) { + (*stream) << "nlines" + << "bcInROF=" << bcInRof + << "nline=" << bcROFNLines[bcInRof] + << "neve=" << neve + << "iteration=" << iteration + << "\n"; + } + } + stream->Close(); + delete stream; +} + +template +void VertexerTraits::debugComputeVertices(int iteration) +{ + auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "update"); + LOGP(info, "writing debug output for computeVertices"); + for (auto rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + (*stream) << "clusterlines" + << "clines_post=" << toSTDVector(mTimeFrame->getTrackletClusters(rofId)) + << "iteration=" << iteration + << "\n"; } - // Cluster merging - std::sort(clusterLines.begin(), clusterLines.end(), [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); - size_t nClusters{clusterLines.size()}; - for (int iCluster1{0}; iCluster1 < nClusters; ++iCluster1) { - std::array vertex1{clusterLines[iCluster1].getVertex()}; - std::array vertex2{}; - for (int iCluster2{iCluster1 + 1}; iCluster2 < nClusters; ++iCluster2) { - vertex2 = clusterLines[iCluster2].getVertex(); - if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { - float distance{(vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0]) + - (vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1]) + - (vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2])}; - if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { - for (auto label : clusterLines[iCluster2].getLabels()) { - clusterLines[iCluster1].add(label, lines[label]); - vertex1 = clusterLines[iCluster1].getVertex(); - } - clusterLines.erase(clusterLines.begin() + iCluster2); - --iCluster2; - --nClusters; + if (mTimeFrame->hasMCinformation()) { + LOGP(info, "\tdumping also MC information"); + const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto irs = dc->getEventRecords(); + int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + o2::steer::MCKinematicsReader mcReader(dc); + + std::map eve2BcInROF, bcInRofNEve; + for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { + auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); + for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { + const auto& ir = irs[eveId2colId[iEve]]; + if (!ir.isDummy()) { // do we need this, is this for diffractive events? + const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); + const int bcInROF = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) % roFrameLengthInBC; + eve2BcInROF[iEve] = bcInROF; + ++bcInRofNEve[bcInROF]; } } } - } - std::sort(clusterLines.begin(), clusterLines.end(), - [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); // ensure clusters are ordered by contributors, so that we can cut after the first. - bool atLeastOneFound{false}; - for (int iCluster{0}; iCluster < nClusters; ++iCluster) { - bool lowMultCandidate{false}; - double beamDistance2{(tf->getBeamX() - clusterLines[iCluster].getVertex()[0]) * (tf->getBeamX() - clusterLines[iCluster].getVertex()[0]) + - (tf->getBeamY() - clusterLines[iCluster].getVertex()[1]) * (tf->getBeamY() - clusterLines[iCluster].getVertex()[1])}; - - if (atLeastOneFound && (lowMultCandidate = clusterLines[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { // We might have pile up with nContr > cut. - lowMultCandidate &= (beamDistance2 < mVrtParams[iteration].lowMultBeamDistCut * mVrtParams[iteration].lowMultBeamDistCut); - if (!lowMultCandidate) { // Not the first cluster and not a low multiplicity candidate, we can remove it - clusterLines.erase(clusterLines.begin() + iCluster); - nClusters--; - continue; + std::unordered_map bcROFNVtx; + std::unordered_map bcROFNPur; + std::unordered_map uniqueVertices; + for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + const auto& pvs = mTimeFrame->getPrimaryVertices(rofId); + const auto& lblspv = mTimeFrame->getPrimaryVerticesMCRecInfo(rofId); + for (int i{0}; i < (int)pvs.size(); ++i) { + const auto& pv = pvs[i]; + const auto& [lbl, pur] = lblspv[i]; + if (lbl.isCorrect()) { + ++uniqueVertices[lbl]; + ++bcROFNVtx[eve2BcInROF[lbl.getEventID()]]; + bcROFNPur[eve2BcInROF[lbl.getEventID()]] += pur; + } } } - if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(clusterLines[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { - atLeastOneFound = true; - ++foundVertices; - vertices.emplace_back(o2::math_utils::Point3D(clusterLines[iCluster].getVertex()[0], - clusterLines[iCluster].getVertex()[1], - clusterLines[iCluster].getVertex()[2]), - clusterLines[iCluster].getRMS2(), // Symm matrix. Diagonal: RMS2 components, - // off-diagonal: square mean of projections on planes. - clusterLines[iCluster].getSize(), // Contributors - clusterLines[iCluster].getAvgDistance2()); // In place of chi2 - vertices.back().setTimeStamp(clusterLines[iCluster].getROF()); - if (labels) { - for (auto& index : clusterLines[iCluster].getLabels()) { - labels->push_back(tf->getLinesLabel(rofId)[index]); // then we can use nContributors from vertices to get the labels + + std::unordered_map bcROFNUVtx, bcROFNCVtx; + for (const auto& [k, _] : eve2BcInROF) { + bcROFNUVtx[k] = bcROFNCVtx[k] = 0; + } + + for (const auto& [lbl, c] : uniqueVertices) { + if (c <= 1) { + ++bcROFNUVtx[eve2BcInROF[lbl.getEventID()]]; + } else { + ++bcROFNCVtx[eve2BcInROF[lbl.getEventID()]]; + } + } + + LOGP(info, "\tdumping nvtx/RofBC"); + for (const auto& [bcInRof, neve] : bcInRofNEve) { + (*stream) << "nvtx" + << "bcInROF=" << bcInRof + << "nvtx=" << bcROFNVtx[bcInRof] // all vertices + << "nuvtx=" << bcROFNUVtx[bcInRof] // unique vertices + << "ncvtx=" << bcROFNCVtx[bcInRof] // cloned vertices + << "npur=" << bcROFNPur[bcInRof] + << "neve=" << neve + << "iteration=" << iteration + << "\n"; + } + + // check dist of clones + std::unordered_map> cVtx; + for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { + const auto& pvs = mTimeFrame->getPrimaryVertices(rofId); + const auto& lblspv = mTimeFrame->getPrimaryVerticesMCRecInfo(rofId); + for (int i{0}; i < (int)pvs.size(); ++i) { + const auto& pv = pvs[i]; + const auto& [lbl, pur] = lblspv[i]; + if (lbl.isCorrect() && uniqueVertices.contains(lbl) && uniqueVertices[lbl] > 1) { + if (!cVtx.contains(lbl)) { + cVtx[lbl] = std::vector(); + } + cVtx[lbl].push_back(pv); } } } - } - verticesInRof.push_back(foundVertices); -} -void VertexerTraits::setNThreads(int n) -{ - if (mNThreads == n && mTaskArena.is_active()) { - return; + for (auto& [_, vertices] : cVtx) { + std::sort(vertices.begin(), vertices.end(), [](const Vertex& a, const Vertex& b) { return a.getNContributors() > b.getNContributors(); }); + for (int i{0}; i < (int)vertices.size(); ++i) { + const auto vtx = vertices[i]; + (*stream) << "cvtx" + << "vertex=" << vtx + << "i=" << i + << "dx=" << vertices[0].getX() - vtx.getX() + << "dy=" << vertices[0].getY() - vtx.getY() + << "dz=" << vertices[0].getZ() - vtx.getZ() + << "drof=" << vertices[0].getTimeStamp().getTimeStamp() - vtx.getTimeStamp().getTimeStamp() + << "dnc=" << vertices[0].getNContributors() - vtx.getNContributors() + << "iteration=" << iteration + << "\n"; + } + } } - mNThreads = n > 0 ? n : 1; -#if defined(VTX_DEBUG) - mNThreads = 1; -#endif - mTaskArena.initialize(mNThreads); - LOGP(info, "Setting seeding vertexer with {} threads.", mNThreads); + stream->Close(); + delete stream; } + +template class VertexerTraits<7>; +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt new file mode 100644 index 0000000000000..818ad1d667371 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/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(boundedmemoryresource + SOURCES testBoundedMemoryResource.cxx + COMPONENT_NAME its-tracking + LABELS "its;tracking" + PUBLIC_LINK_LIBRARIES O2::ITStracking) diff --git a/Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx b/Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx new file mode 100644 index 0000000000000..aae28f5cbc36e --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx @@ -0,0 +1,190 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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 "ITStracking/BoundedAllocator.h" + +using namespace o2::its; +using Vec = bounded_vector; +auto getRandomInt(int min = -100, int max = 100) +{ + static std::mt19937 gen(std::random_device{}()); // static generator, seeded once + std::uniform_int_distribution<> dist(min, max); + return [&, dist]() mutable { + return dist(gen); + }; +} + +// -------- Throwing upstream resource for testing rollback -------- +class ThrowingResource final : public std::pmr::memory_resource +{ + protected: + void* do_allocate(size_t, size_t) final + { + throw std::bad_alloc(); // always fail + } + void do_deallocate(void*, size_t, size_t) noexcept final + { + // nothing + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } +}; + +// -------- Upstream resource with empty deallocate -------- +class NoDeallocateResource final : public std::pmr::memory_resource +{ + public: + NoDeallocateResource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) + : mUpstream(upstream) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) final + { + return mUpstream->allocate(bytes, alignment); + } + void do_deallocate(void*, size_t, size_t) noexcept final + { + // nothing + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } + + private: + std::pmr::memory_resource* mUpstream; +}; + +// -------- Tests -------- +BOOST_AUTO_TEST_CASE(allocation_and_clear_updates_used_memory) +{ + BoundedMemoryResource bmr(10 * 1024 * 1024); // 10 MB cap + + Vec v(std::pmr::polymorphic_allocator{&bmr}); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); + + const size_t count = 128; + v.reserve(count); + const size_t expected = count * sizeof(int); + BOOST_CHECK_GE(bmr.getUsedMemory(), expected); + BOOST_CHECK_LE(bmr.getUsedMemory(), expected + 64); + + deepVectorClear(v, &bmr); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(clearResizeBoundedVector_resizes_and_tracks_memory) +{ + BoundedMemoryResource bmr(1024 * 1024); // 1 MB cap + + Vec v(std::pmr::polymorphic_allocator{&bmr}); + v.reserve(200); + const size_t used_before = bmr.getUsedMemory(); + BOOST_CHECK_GT(used_before, 0u); + + clearResizeBoundedVector(v, 50, &bmr, 7); + const size_t used_after = bmr.getUsedMemory(); + BOOST_CHECK_GE(used_after, 50 * sizeof(int)); + BOOST_CHECK_LT(used_after, used_before); + + clearResizeBoundedVector(v, 300, &bmr, 3); + BOOST_CHECK_GE(bmr.getUsedMemory(), 300 * sizeof(int)); +} + +BOOST_AUTO_TEST_CASE(upstream_throw_rolls_back_reservation) +{ + ThrowingResource upstream; + BoundedMemoryResource bmr(std::numeric_limits::max(), &upstream); + const size_t bytes = 1024; + bool threw = false; + void* p{nullptr}; + try { + p = bmr.allocate(bytes, alignof(std::max_align_t)); + } catch (const std::bad_alloc&) { + threw = true; + } + BOOST_CHECK(threw); + BOOST_CHECK_EQUAL(p, nullptr); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(vector_of_bounded_vectors_deep_clear_releases_all) +{ + BoundedMemoryResource bmr(10 * 1024 * 1024); // 10 MB + std::vector outer; + outer.reserve(5); + for (int i = 0; i < 5; ++i) { + outer.emplace_back(std::pmr::polymorphic_allocator{&bmr}); + outer.back().reserve(100); + } + BOOST_CHECK_GT(bmr.getUsedMemory(), 0u); + deepVectorClear(outer, &bmr); // deep clear outer + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(array_of_bounded_vectors_clear_resize_works) +{ + BoundedMemoryResource bmr(10 * 1024 * 1024); + std::array arr{{Vec(std::pmr::polymorphic_allocator{&bmr}), + Vec(std::pmr::polymorphic_allocator{&bmr}), + Vec(std::pmr::polymorphic_allocator{&bmr})}}; + clearResizeBoundedVector(arr[0], 10, &bmr, 1); + clearResizeBoundedVector(arr[1], 20, &bmr, 2); + clearResizeBoundedVector(arr[2], 30, &bmr, 3); + BOOST_CHECK_GT(bmr.getUsedMemory(), 0u); + deepVectorClear(arr, &bmr); // now clear all recursively + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(deepVectorClear_releases_and_reuses_resource) +{ + // Use a small bounded memory resource + BoundedMemoryResource bmr(1024); + bounded_vector vec{std::pmr::polymorphic_allocator{&bmr}}; + vec.resize(100, 42); + BOOST_TEST(bmr.getUsedMemory() > 0); + deepVectorClear(vec, &bmr); + BOOST_TEST(vec.empty()); + BOOST_TEST(vec.get_allocator().resource() == &bmr); + auto usedAfter = bmr.getUsedMemory(); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0); + vec.push_back(7); + BOOST_TEST(vec.size() == 1); + BOOST_TEST(vec[0] == 7); + BOOST_TEST(vec.get_allocator().resource() == &bmr); +} + +BOOST_AUTO_TEST_CASE(clear_with_memory_resource_without_deallocator) +{ + NoDeallocateResource dmr; + Vec v(std::pmr::polymorphic_allocator{&dmr}); + + for (int shift{0}; shift < 12; ++shift) { + const int c{1 << shift}; + v.resize(100); + std::generate(v.begin(), v.end(), getRandomInt()); + // allocate different sizes, which is actually a no-op now + clearResizeBoundedVector(v, c / 2, &dmr, 999); + for (size_t i{0}; i < c / 2; ++i) { // now only the first c/2 elements should be set + BOOST_CHECK_EQUAL(v[i], 999); + } + // try to deepclear + deepVectorClear(v); + } +} diff --git a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index 3609560eccf72..10e16e49d92b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -13,10 +13,7 @@ o2_add_library(ITSWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx src/ClusterWriterWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx - src/CookedTrackerSpec.cxx src/TrackWriterSpec.cxx src/TrackReaderSpec.cxx src/VertexReaderSpec.cxx diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h deleted file mode 100644 index c5038c87fa467..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.h - -#ifndef O2_ITS_CLUSTERERDPL -#define O2_ITS_CLUSTERERDPL - -#include -#include "DetectorsBase/GRPGeomHelper.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -using namespace o2::framework; - -namespace o2 -{ - -namespace itsmft -{ -class Clusterer; -} - -namespace its -{ - -class ClustererDPL : public Task -{ - public: - ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} - ~ClustererDPL() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; - void endOfStream(o2::framework::EndOfStreamContext& ec) final; - - private: - void updateTimeDependentParams(ProcessingContext& pc); - - int mState = 0; - bool mUseMC = true; - bool mUseClusterDictionary = true; - int mNThreads = 1; - std::unique_ptr mFile = nullptr; - std::unique_ptr mClusterer = nullptr; - std::shared_ptr mGGCCDBRequest; -}; - -/// create a processor spec -/// run ITS cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); - -} // namespace its -} // namespace o2 - -#endif /* O2_ITS_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/CookedTrackerSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/CookedTrackerSpec.h deleted file mode 100644 index 02e278eeedda9..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/CookedTrackerSpec.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 CookedTrackerSpec.h - -#ifndef O2_ITS_COOKEDTRACKERDPL -#define O2_ITS_COOKEDTRACKERDPL - -#include "Framework/DataProcessorSpec.h" -#include "ITSReconstruction/CookedTracker.h" -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Vertexer.h" -#include "ITStracking/VertexerTraits.h" -#include "DataFormatsParameters/GRPObject.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "Framework/Task.h" -#include "TStopwatch.h" -#include "DetectorsBase/GRPGeomHelper.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -class CookedTrackerDPL : public Task -{ - public: - CookedTrackerDPL(std::shared_ptr gr, bool useMC, int trgType, const TrackingMode& trMode); - ~CookedTrackerDPL() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - void endOfStream(framework::EndOfStreamContext& ec) final; - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; - void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } - - private: - void updateTimeDependentParams(ProcessingContext& pc); - - std::shared_ptr mGGCCDBRequest; - int mState = 0; - bool mUseMC = true; - bool mRunVertexer = true; - int mUseTriggers = 0; - TrackingMode mMode = TrackingMode::Sync; - const o2::itsmft::TopologyDictionary* mDict = nullptr; - std::unique_ptr mGRP = nullptr; - o2::its::CookedTracker mTracker; - std::unique_ptr mVertexerTraitsPtr = nullptr; - std::unique_ptr mVertexerPtr = nullptr; - TStopwatch mTimer; -}; - -/// create a processor spec -/// run ITS CookedMatrix tracker -framework::DataProcessorSpec getCookedTrackerSpec(bool useMC, bool useGeom, int useTrig, const std::string& trMode); - -} // namespace its -} // namespace o2 - -#endif /* O2_ITS_COOKEDTRACKERDPL */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h index eaacfab10f886..cd18fd459546d 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h @@ -136,7 +136,7 @@ class ITSDCSParser : public Task std::string mCcdbUrl = ""; // Vector containing all the staves listed in the EOR file - std::vector mSavedStaves = {}; + std::vector mSavedStaves = {}; // Disabled chip map o2::itsmft::NoiseMap mDeadMap; diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h index 7f9efa2098893..1d5d829a6f79a 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h @@ -15,8 +15,8 @@ /// @file RecoWorkflow.h #include "Framework/WorkflowSpec.h" - -#include "GPUDataTypes.h" +#include "ITStracking/Configuration.h" +#include "GPUDataTypesConfig.h" namespace o2 { @@ -26,9 +26,9 @@ namespace its namespace reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, const std::string& trmode, const bool overrideBeamPosition = false, +framework::WorkflowSpec getWorkflow(bool useMC, TrackingMode::Type trmode, const bool overrideBeamPosition = false, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, bool useGeom = false, int useTrig = 0, - bool useGPUWF = false, o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU); + bool useGPUWF = false, o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); } } // namespace its diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h index 2a139f7997dfb..a768b848c7095 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h @@ -175,6 +175,7 @@ class ITSThresholdCalibrator : public Task unsigned char vCharge[N_COL]; unsigned char vHits[N_COL]; short int mColStep = 8; // save s-curves to tree every mColStep pixels on 1 row + short int mRowStep = 1; // Initialize pointers for doing error function fits TH1F* mFitHist = nullptr; @@ -232,7 +233,8 @@ class ITSThresholdCalibrator : public Task short int mRunTypeUp = -1; short int mRunTypeRU[N_RU] = {0}; short int mRunTypeRUCopy[N_RU] = {0}; - short int mCdwCntRU[N_RU][N_ROW] = {{0}}; + bool mFlagsRU[N_RU] = {0}; + std::map, 500>>> mCdwCntRU; // RU --> row --> 2D hit map short int mLoopVal[N_RU][N_ROW] = {{0}}; bool mActiveLinks[N_RU][3] = {{false}}; std::set mRuSet; @@ -241,6 +243,7 @@ class ITSThresholdCalibrator : public Task short int mMin = -1, mMax = -1, mMin2 = 0, mMax2 = 0; short int mStep = 1, mStep2 = 1; short int mStrobeWindow = 5; // 5 means 5*25ns = 125 ns + short int mRowScan = 512; // number of scanned rows, used only to normalize % of success // Get threshold method (fit == 1, derivative == 0, or hitcounting == 2) char mFitType = -1; @@ -293,6 +296,7 @@ class ITSThresholdCalibrator : public Task short int manualStep = 1, manualStep2 = 1; std::string manualScanType; short int manualStrobeWindow = 5; + short int manualRowScan = 512; // used only to normalize % of success in thr/ithr/vcasn scans // for CRU_ITS data processing bool isCRUITS = false; @@ -306,7 +310,7 @@ class ITSThresholdCalibrator : public Task int maxDumpS = -1; // maximum number of s-curves to be dumped, default -1 = dump all std::string chipDumpS = ""; // list of comma-separated O2 chipIDs to be dumped, default is empty = dump all int dumpCounterS[24120] = {0}; // count dumps for every chip - int countCdw[24120] = {0}; // count how many CDWs have been processed with the maximum charge injected: usefull for s-curve dump when hits do not arrive in order + bool isChipDB[24120] = {0}; // check whether a chip has been already added to DB entry TFile* fileDumpS; // file where to store the s-curves on disk std::vector chipDumpList; // vector of chips to dump @@ -324,6 +328,9 @@ class ITSThresholdCalibrator : public Task // Percentage cut for VCASN/ITHR scans short int mPercentageCut = 25; // default, at least 1 good row equivalent + + // For data replay only + short int isLocal = false; }; // Create a processor spec diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h index 600e42e136697..8666864ca1ae9 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h @@ -20,11 +20,11 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Headers/DataHeader.h" +#include "ITStracking/Definitions.h" #include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "DataFormatsITSMFT/ROFRecord.h" -#include "ReconstructionDataFormats/Vertex.h" namespace o2 { @@ -33,8 +33,6 @@ namespace its class TrackReader : public o2::framework::Task { - using Vertex = o2::dataformats::Vertex>; - public: TrackReader(bool useMC = true); ~TrackReader() override = default; diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h index 27c4174fab244..01eb7cb7b69aa 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h @@ -23,11 +23,17 @@ #include "ITStracking/TrackingInterface.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TStopwatch.h" +namespace o2::gpu +{ +class GPUReconstruction; +class GPUChainITS; +} // namespace o2::gpu + namespace o2::its { @@ -37,9 +43,9 @@ class TrackerDPL : public framework::Task TrackerDPL(std::shared_ptr gr, bool isMC, int trgType, - const TrackingMode& trMode = TrackingMode::Unset, + const TrackingMode::Type trMode = TrackingMode::Unset, const bool overrBeamEst = false, - o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU); + o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; @@ -57,8 +63,7 @@ class TrackerDPL : public framework::Task TStopwatch mTimer; }; -using o2::its::TrackingMode; -framework::DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int useTrig, const std::string& trMode, const bool overrBeamEst = false, o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int useTrig, TrackingMode::Type trMode, const bool overrBeamEst = false, o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h index f412640a702ef..b300967408256 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h @@ -19,7 +19,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include "ReconstructionDataFormats/Vertex.h" +#include "ITStracking/Definitions.h" #include "DataFormatsITSMFT/ROFRecord.h" namespace o2 @@ -30,8 +30,6 @@ namespace its class VertexReader : public o2::framework::Task { - using Vertex = o2::dataformats::Vertex>; - public: VertexReader() = default; ~VertexReader() override = default; diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index 4dffbaf88893c..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.cxx - -#include - -#include "ITSWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "ITSClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("its-cluster-writer", - "o2clus_its.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with ITS clusters"}, - BranchDefinition{InputSpec{"compclus", "ITS", "COMPCLUSTERS", 0}, - "ITSClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "ITS", "PATTERNS", 0}, - "ITSClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "ITS", "CLUSTERSROF", 0}, - "ITSClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "ITS", "CLUSTERSMCTR", 0}, - "ITSClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "ITS", "CLUSTERSMC2ROF", 0}, - "ITSClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx index ca5db7acd63e1..aba468b3e9460 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -12,7 +12,7 @@ /// @file ClusterWriterWorkflow.cxx #include "ITSWorkflow/ClusterWriterWorkflow.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" namespace o2 { @@ -26,7 +26,7 @@ framework::WorkflowSpec getWorkflow(bool useMC) { framework::WorkflowSpec specs; - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx deleted file mode 100644 index d58e4f5d915c1..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "ITSMFTReconstruction/ClustererParam.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace its -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingITS::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - LOGP(info, "Initialising ITSClusterer with {} threads", mNThreads); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(info) << "ITSClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - LOG(info) << "ITSClusterer pulled " << labels.getNElements() << " labels "; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginITS; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(info) << "ITSClusterer pushed " << clusCompVec.size() << " clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::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; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(clParams.maxBCDiffToSquashBias > 0 ? nROFsToSquash : 0); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const o2::itsmft::TopologyDictionary*)obj); - } - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "ITS", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "ITS", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/ClustererParam")); - inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "ITS", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "its-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -///_______________________________________ -void ClustererDPL::endOfStream(o2::framework::EndOfStreamContext& ec) -{ - mClusterer->print(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx deleted file mode 100644 index 4a0470adcf07a..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file CookedTrackerSpec.cxx - -#include - -#include "TGeoGlobalMagField.h" - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "ITSWorkflow/CookedTrackerSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/Cluster.h" -#include "DataFormatsITS/TrackITS.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include -#include "ITSMFTBase/DPLAlpideParam.h" -#include "DataFormatsTRD/TriggerRecord.h" - -#include "Field/MagneticField.h" -#include "DetectorsBase/GeometryManager.h" -#include "DetectorsBase/Propagator.h" -#include "ITSBase/GeometryTGeo.h" -#include "CommonDataFormat/IRFrame.h" -#include "ITStracking/ROframe.h" -#include "ITStracking/IOUtils.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "CommonUtils/StringUtils.h" - -#include "ITSReconstruction/FastMultEstConfig.h" -#include "ITSReconstruction/FastMultEst.h" -#include "ITSMFTReconstruction/ClustererParam.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -using Vertex = o2::dataformats::Vertex>; - -CookedTrackerDPL::CookedTrackerDPL(std::shared_ptr gr, bool useMC, int trgType, const TrackingMode& trMode) : mGGCCDBRequest(gr), mUseMC(useMC), mUseTriggers{trgType}, mMode(trMode) -{ - mVertexerTraitsPtr = std::make_unique(); - mVertexerPtr = std::make_unique(mVertexerTraitsPtr.get()); -} - -void CookedTrackerDPL::init(InitContext& ic) -{ - mTimer.Stop(); - mTimer.Reset(); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - auto nthreads = ic.options().get("nthreads"); - mTracker.setNumberOfThreads(nthreads); -} - -void CookedTrackerDPL::run(ProcessingContext& pc) -{ - mTimer.Start(false); - updateTimeDependentParams(pc); - auto compClusters = pc.inputs().get>("compClusters"); - gsl::span patterns = pc.inputs().get>("patterns"); - - // code further down does assignment to the rofs and the altered object is used for output - // we therefore need a copy of the vector rather than an object created directly on the input data, - // the output vector however is created directly inside the message memory thus avoiding copy by - // snapshot - auto rofsinput = pc.inputs().get>("ROframes"); - gsl::span physTriggers; - std::vector fromTRD; - if (mUseTriggers == 2) { // use TRD triggers - o2::InteractionRecord ir{0, pc.services().get().firstTForbit}; - auto trdTriggers = pc.inputs().get>("phystrig"); - for (const auto& trig : trdTriggers) { - if (trig.getBCData() >= ir && trig.getNumberOfTracklets()) { - ir = trig.getBCData(); - fromTRD.emplace_back(o2::itsmft::PhysTrigger{ir, 0}); - } - } - physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); - } else if (mUseTriggers == 1) { // use Phys triggers from ITS stream - physTriggers = pc.inputs().get>("phystrig"); - } - - auto& rofs = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0}, rofsinput.begin(), rofsinput.end()); - - std::unique_ptr> labels; - gsl::span mc2rofs; - if (mUseMC) { - labels = pc.inputs().get*>("labels"); - // get the array as read-onlt span, a snapshot is send forward - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - TimeFrame mTimeFrame; - - LOG(info) << "ITSCookedTracker pulled " << compClusters.size() << " clusters, in " << rofs.size() << " RO frames"; - - std::vector trackLabels; - if (mUseMC) { - mTracker.setMCTruthContainers(labels.get(), &trackLabels); - } - - o2::its::ROframe event(0, 7); - mVertexerPtr->adoptTimeFrame(mTimeFrame); - - auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0}); - auto& vertices = pc.outputs().make>(Output{"ITS", "VERTICES", 0}); - auto& tracks = pc.outputs().make>(Output{"ITS", "TRACKS", 0}); - auto& clusIdx = pc.outputs().make>(Output{"ITS", "TRACKCLSID", 0}); - auto& irFrames = pc.outputs().make>(Output{"ITS", "IRFRAMES", 0}); - - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); // RS: this should come from CCDB - int nBCPerTF = mTracker.getContinuousMode() ? alpParams.roFrameLengthInBC : alpParams.roFrameLengthTrig; - - gsl::span::iterator pattIt_timeframe = patterns.begin(); - gsl::span::iterator pattIt_tracker = patterns.begin(); - gsl::span rofspan(rofs); - mTimeFrame.loadROFrameData(rofspan, compClusters, pattIt_timeframe, mDict, labels.get()); - - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - FastMultEst multEst; // mult estimator - std::vector processingMask; - int cutVertexMult{0}, cutRandomMult = int(rofsinput.size()) - multEst.selectROFs(rofsinput, compClusters, physTriggers, processingMask); - - // auto processingMask_ephemeral = processingMask; - mTimeFrame.setMultiplicityCutMask(processingMask); - float vertexerElapsedTime; - if (mRunVertexer) { - vertexerElapsedTime = mVertexerPtr->clustersToVertices([&](std::string s) { LOG(info) << s; }); - } - LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms in {} ROFs", vertexerElapsedTime, rofspan.size()); - for (size_t iRof{0}; iRof < rofspan.size(); ++iRof) { - auto& rof = rofspan[iRof]; - - auto& vtxROF = vertROFvec.emplace_back(rof); // register entry and number of vertices in the - vtxROF.setFirstEntry(vertices.size()); - vtxROF.setNEntries(0); - if (!processingMask[iRof]) { - rof.setFirstEntry(tracks.size()); - rof.setNEntries(0); - continue; - } - - std::vector>> vtxVecLoc; - for (auto& v : mTimeFrame.getPrimaryVertices(iRof)) { - vtxVecLoc.push_back(v); - } - - if (multEstConf.isVtxMultCutRequested()) { // cut was requested - std::vector>> vtxVecSel; - vtxVecSel.swap(vtxVecLoc); - int nv = vtxVecSel.size(), nrej = 0; - for (const auto& vtx : vtxVecSel) { - if (!multEstConf.isPassingVtxMultCut(vtx.getNContributors())) { - LOG(info) << "Found vertex mult. " << vtx.getNContributors() << " is outside of requested range " << multEstConf.cutMultVtxLow << " : " << multEstConf.cutMultVtxHigh << " | ROF " << rof.getBCData(); - nrej++; - continue; // skip vertex of unwanted multiplicity - } - vtxVecLoc.push_back(vtx); - } - if (nv && (nrej == nv)) { // all vertices were rejected - cutVertexMult++; - processingMask[iRof] = false; - } - } - if (vtxVecLoc.empty()) { - if (multEstConf.cutMultVtxLow < 1) { // do blind search only if there is no cut on the low mult vertices - vtxVecLoc.emplace_back(); - } else { - rof.setFirstEntry(tracks.size()); - rof.setNEntries(0); - continue; - } - } else { // save vertices - vtxROF.setNEntries(vtxVecLoc.size()); - for (const auto& vtx : vtxVecLoc) { - vertices.push_back(vtx); - } - } - - mTracker.setVertices(vtxVecLoc); - mTracker.process(compClusters, pattIt_tracker, mDict, tracks, clusIdx, rof); - if (processingMask[iRof]) { - irFrames.emplace_back(rof.getBCData(), rof.getBCData() + nBCPerTF - 1).info = tracks.size(); - } - } - LOGP(info, " - rejected {}/{} ROFs: random/mult.sel:{} (seed {}), vtx.sel:{}", cutRandomMult + cutVertexMult, rofspan.size(), cutRandomMult, multEst.lastRandomSeed, cutVertexMult); - LOG(info) << "ITSCookedTracker pushed " << tracks.size() << " tracks and " << vertices.size() << " vertices"; - - if (mUseMC) { - pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0}, trackLabels); - pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0}, mc2rofs); - } - mTimer.Stop(); -} - -void CookedTrackerDPL::endOfStream(EndOfStreamContext& ec) -{ - LOGF(info, "ITS Cooked-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -///_______________________________________ -void CookedTrackerDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - if (pc.inputs().getPos("itsTGeo") >= 0) { - pc.inputs().get("itsTGeo"); - } - mVertexerPtr->getGlobalConfiguration(); - o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, - o2::math_utils::TransformType::T2G)); - mTracker.setGeometry(geom); - mTracker.setConfigParams(); - LOG(info) << "Tracking mode " << mMode; - if (mMode == TrackingMode::Cosmics) { - LOG(info) << "Setting cosmics parameters..."; - mTracker.setParametersCosmics(); - mRunVertexer = false; - } - mTracker.setBz(o2::base::Propagator::Instance()->getNominalBz()); - bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS); - LOG(info) << "ITSCookedTracker RO: continuous=" << continuous; - mTracker.setContinuousMode(continuous); - } -} - -///_______________________________________ -void CookedTrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated"; - setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { - LOG(info) << "ITS GeometryTGeo loaded from ccdb"; - o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); - return; - } -} - -DataProcessorSpec getCookedTrackerSpec(bool useMC, bool useGeom, int trgType, const std::string& trModeS) -{ - std::vector inputs; - inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", "ITS", "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); - if (trgType == 1) { - inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe); - } else if (trgType == 2) { - inputs.emplace_back("phystrig", "TRD", "TRKTRGRD", 0, Lifetime::Timeframe); - } - - std::vector outputs; - outputs.emplace_back("ITS", "TRACKS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "TRACKCLSID", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "ITSTrackROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "VERTICES", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "VERTICESROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "IRFRAMES", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe); - } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - if (!useGeom) { - ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, inputs); - } - return DataProcessorSpec{ - "its-cooked-tracker", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - trgType, - trModeS == "sync" ? o2::its::TrackingMode::Sync : trModeS == "async" ? o2::its::TrackingMode::Async - : o2::its::TrackingMode::Cosmics)}, - Options{{"nthreads", VariantType::Int, 1, {"Number of threads"}}}}; -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx index a9e1121077f02..d504640588023 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx @@ -386,9 +386,9 @@ void ITSDCSParser::saveToOutput() void ITSDCSParser::saveMissingToOutput() { // Loop on the missing staves - std::vector missingStaves; - std::vector listStaves = this->listStaves(); - std::vector savedStaves = this->mSavedStaves; + std::vector missingStaves; + std::vector listStaves = this->listStaves(); + std::vector savedStaves = this->mSavedStaves; std::sort(savedStaves.begin(), savedStaves.end()); std::set_difference(listStaves.begin(), listStaves.end(), savedStaves.begin(), savedStaves.end(), std::inserter(missingStaves, missingStaves.begin())); @@ -557,7 +557,7 @@ std::vector ITSDCSParser::listStaves() std::string stavenum = ""; for (int i = 0; i < 7; i++) { for (int j = 0; j < stavesPerLayer[i]; j++) { - string stavestring = std::to_string(j); + std::string stavestring = std::to_string(j); int precision = 2 - std::min(2, (int)(stavestring.size())); stavenum = std::string(precision, '0').append(std::to_string(j)); std::string stave = "L" + std::to_string(i) + "_" + stavenum; diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 05e873c18b898..9f8cb6c83ef99 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -12,31 +12,23 @@ /// @file RecoWorkflow.cxx #include "ITSWorkflow/RecoWorkflow.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" -#include "ITSWorkflow/CookedTrackerSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" +#include "ITStracking/TrackingConfigParam.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" #include "GlobalTrackingWorkflowWriters/IRFrameWriterSpec.h" #include "GPUWorkflow/GPUWorkflowSpec.h" #include "Framework/CCDBParamSpec.h" // Dummy TPC completion policy data -using CompletionPolicyData = std::vector; -static CompletionPolicyData gPolicyData; -static std::shared_ptr gTask; -namespace o2 -{ -namespace its -{ -namespace reco_workflow +namespace o2::its::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, - bool useCAtracker, - const std::string& trmode, + TrackingMode::Type trmode, const bool overrideBeamPosition, bool upstreamDigits, bool upstreamClusters, @@ -44,52 +36,49 @@ framework::WorkflowSpec getWorkflow(bool useMC, bool useGeom, int useTrig, bool useGPUWF, - o2::gpu::GPUDataTypes::DeviceType dtype) + o2::gpu::gpudatatypes::DeviceType dtype) { framework::WorkflowSpec specs; if (!(upstreamDigits || upstreamClusters)) { specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root")); } if (!upstreamClusters) { - specs.emplace_back(o2::its::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } - if (!trmode.empty()) { - if (useCAtracker) { - if (useGPUWF) { - o2::gpu::GPURecoWorkflowSpec::Config cfg; - cfg.runITSTracking = true; - cfg.itsTriggerType = useTrig; - cfg.itsOverrBeamEst = overrideBeamPosition; - cfg.processMC = useMC; + if ((trmode != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { + if (useGPUWF) { + o2::gpu::GPURecoWorkflowSpec::Config cfg{ + .itsTriggerType = useTrig, + .processMC = useMC, + .runITSTracking = true, + .itsOverrBeamEst = overrideBeamPosition, + }; - Inputs ggInputs; - auto ggRequest = std::make_shared(false, true, false, true, true, - useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, - ggInputs, true); - if (!useGeom) { - ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, ggInputs); - } + Inputs ggInputs; + auto ggRequest = std::make_shared(false, true, false, true, true, + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, + ggInputs, true); + if (!useGeom) { + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, ggInputs); + } - auto task = std::make_shared(&gPolicyData, cfg, std::vector(), 0, ggRequest); - gTask = task; - Inputs taskInputs = task->inputs(); - Options taskOptions = task->options(); - std::move(ggInputs.begin(), ggInputs.end(), std::back_inserter(taskInputs)); + static std::vector policyData; + static std::shared_ptr task = std::make_shared(&policyData, cfg, std::vector(), 0, ggRequest); + Inputs taskInputs = task->inputs(); + Options taskOptions = task->options(); + std::move(ggInputs.begin(), ggInputs.end(), std::back_inserter(taskInputs)); - specs.emplace_back(DataProcessorSpec{ - "its-gpu-tracker", - taskInputs, - task->outputs(), - AlgorithmSpec{adoptTask(task)}, - taskOptions}); - } else { - specs.emplace_back(o2::its::getTrackerSpec(useMC, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); - } + specs.emplace_back(DataProcessorSpec{ + .name = "its-gpu-tracker", + .inputs = taskInputs, + .outputs = task->outputs(), + .algorithm = AlgorithmSpec{adoptTask(task)}, + .options = taskOptions}); } else { - specs.emplace_back(o2::its::getCookedTrackerSpec(useMC, useGeom, useTrig, trmode)); + specs.emplace_back(o2::its::getTrackerSpec(useMC, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); } if (!disableRootOutput) { specs.emplace_back(o2::its::getTrackWriterSpec(useMC)); @@ -99,6 +88,4 @@ framework::WorkflowSpec getWorkflow(bool useMC, return specs; } -} // namespace reco_workflow -} // namespace its -} // namespace o2 +} // namespace o2::its::reco_workflow diff --git a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx index e1d7dc725e9e3..ce0b840f4a037 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx @@ -74,6 +74,8 @@ void ITSThresholdCalibrator::init(InitContext& ic) LOG(warning) << "mColStep = " << mColStep << ": saving s-curves of only 1 pixel (pix 0) per row"; } + isLocal = ic.options().get("local-processing"); + std::string fittype = ic.options().get("fittype"); if (fittype == "derivative") { this->mFitType = DERIVATIVE; @@ -149,6 +151,12 @@ void ITSThresholdCalibrator::init(InitContext& ic) // Parameters to operate in manual mode (when run type is not recognized automatically) isManualMode = ic.options().get("manual-mode"); if (isManualMode) { + try { + manualRowScan = ic.options().get("manual-rowscan"); + } catch (std::exception const& e) { + throw std::runtime_error("Number of scanned rows not found, mandatory in manual mode"); + } + try { manualMin = ic.options().get("manual-min"); } catch (std::exception const& e) { @@ -746,39 +754,40 @@ void ITSThresholdCalibrator::extractThresholdRow(const short int& chipID, const } else { // threshold, vcasn, ithr, vresetd_2d - short int iRU = getRUID(chipID); + for (int scan_i = 0; scan_i < ((mScanType == 'r') ? N_RANGE : N_RANGE2); scan_i++) { #ifdef WITH_OPENMP - omp_set_num_threads(mNThreads); + omp_set_num_threads(mNThreads); #pragma omp parallel for schedule(dynamic) #endif - // Loop over all columns (pixels) in the row - for (short int col_i = 0; col_i < this->N_COL; col_i++) { - // Do the threshold fit - float thresh = 0., noise = 0.; - bool success = false; - int spoints = 0; - int scan_i = mScanType == 'r' ? (mLoopVal[iRU][row] - mMin) / mStep : 0; - if (isDumpS) { // already protected for multi-thread in the init - mFitHist->SetName(Form("scurve_chip%d_row%d_col%d_scani%d", chipID, row, col_i, scan_i)); - } + // Loop over all columns (pixels) in the row + for (short int col_i = 0; col_i < this->N_COL; col_i++) { + // Do the threshold fit + float thresh = 0., noise = 0.; + bool success = false; + int spoints = 0; - success = this->findThreshold(chipID, mPixelHits[chipID][row][col_i], - this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, spoints, scan_i); + if (isDumpS) { // already protected for multi-thread in the init + mFitHist->SetName(Form("scurve_chip%d_row%d_col%d_scani%d", chipID, row, col_i, scan_i)); + } - vChipid[col_i] = chipID; - vRow[col_i] = row; - vThreshold[col_i] = (mScanType == 'T' || mScanType == 'r') ? (short int)(thresh * 10.) : (short int)(thresh); - vNoise[col_i] = (float)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros - vSuccess[col_i] = success; - vPoints[col_i] = spoints > 0 ? (unsigned char)(spoints) : 0; + success = this->findThreshold(chipID, mPixelHits[chipID][row][col_i], + this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, spoints, scan_i); + vChipid[col_i] = chipID; + vRow[col_i] = row; + vThreshold[col_i] = (mScanType == 'T' || mScanType == 'r') ? (short int)(thresh * 10.) : (short int)(thresh); + vNoise[col_i] = (float)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros + vSuccess[col_i] = success; + vPoints[col_i] = spoints > 0 ? (unsigned char)(spoints) : 0; + + if (mScanType == 'r') { + vMixData[col_i] = (scan_i * mStep) + mMin; + } + } if (mScanType == 'r') { - vMixData[col_i] = mLoopVal[iRU][row]; + this->saveThreshold(); // save before moving to the next vresetd } } - if (mScanType == 'r') { - this->saveThreshold(); // save before moving to the next vresetd - } // Fill the ScTree tree if (mScanType == 'T' || mScanType == 'V' || mScanType == 'I') { // TODO: store also for other scans? @@ -794,6 +803,9 @@ void ITSThresholdCalibrator::extractThresholdRow(const short int& chipID, const // Saves threshold information to internal memory if (mScanType != 'P' && mScanType != 'p' && mScanType != 't' && mScanType != 'R' && mScanType != 'r') { + if (mVerboseOutput) { + LOG(info) << "Saving data of ChipID: " << chipID << " Row: " << row; + } this->saveThreshold(); } } @@ -934,6 +946,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->mMax = 50; this->N_RANGE = 51; this->mCheckExactRow = true; + mRowScan = 512; } else if (runtype == THR_SCAN_SHORT || runtype == THR_SCAN_SHORT_100HZ || runtype == THR_SCAN_SHORT_200HZ || runtype == THR_SCAN_SHORT_33 || runtype == THR_SCAN_SHORT_2_10HZ || runtype == THR_SCAN_SHORT_150INJ) { @@ -951,6 +964,12 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) nInjScaled = nInj / 3; } } + mRowScan = 11; + if (runtype == THR_SCAN_SHORT_33) { + mRowScan = 33; + } else if (runtype == THR_SCAN_SHORT_2_10HZ) { + mRowScan = 2; + } } else if (runtype == VCASN150 || runtype == VCASN100 || runtype == VCASN100_100HZ || runtype == VCASN130 || runtype == VCASNBB) { // VCASN tuning for different target thresholds // Store average VCASN for each chip into CCDB @@ -962,6 +981,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->mMax = inMaxVcasn; // 80 is the default this->N_RANGE = mMax - mMin + 1; this->mCheckExactRow = true; + mRowScan = 4; } else if (runtype == ITHR150 || runtype == ITHR100 || runtype == ITHR100_100HZ || runtype == ITHR130) { // ITHR tuning -- average ITHR per chip @@ -973,6 +993,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->mMax = inMaxIthr; // 100 is the default this->N_RANGE = mMax - mMin + 1; this->mCheckExactRow = true; + mRowScan = 4; } else if (runtype == DIGITAL_SCAN || runtype == DIGITAL_SCAN_100HZ || runtype == DIGITAL_SCAN_NOMASK) { // Digital scan -- only storing one value per chip, no fit needed @@ -983,6 +1004,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->mMax = 0; this->N_RANGE = mMax - mMin + 1; this->mCheckExactRow = false; + mRowStep = 1; } else if (runtype == ANALOGUE_SCAN) { // Analogue scan -- only storing one value per chip, no fit needed @@ -1078,6 +1100,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) if (scaleNinj) { nInjScaled = nInj / 3; } + mRowScan = manualRowScan; } else { throw runtype; } @@ -1284,7 +1307,7 @@ void ITSThresholdCalibrator::extractAndUpdate(const short int& chipID, const sho return; } -////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// // Main running function // Get info from previous stf decoder workflow, then loop over readout frames // (ROFs) to count hits and extract thresholds @@ -1367,15 +1390,13 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) row = !mCdwVersion ? (short int)(calib.calibUserField & 0xffff) : (short int)(calib.calibUserField & 0x1ff); // cw counter cwcnt = (short int)(calib.calibCounter); - // count the last N injections - short int checkVal = (mScanType == 'I') ? mMin : mMax; - if ((mScanType != 'r' && mScanType != 'p' && mScanType != 't' && loopval == checkVal) || - (mScanType == 'r' && realcharge == mMax2) || - (mScanType == 'p' && realcharge == mMin2) || - (mScanType == 't' && loopval == checkVal && realcharge == mMax2)) { - mCdwCntRU[iRU][row]++; - mLoopVal[iRU][row] = loopval; // keep loop val (relevant for VRESET2D and TOT_1ROW scan only) - } + + // count injections + short int loopPoint = (loopval - this->mMin) / mStep; + short int chgPoint = (realcharge - this->mMin2) / mStep2; + auto& arr = mCdwCntRU[iRU][row]; + arr[chgPoint][loopPoint]++; + if (this->mVerboseOutput) { LOG(info) << "RU: " << iRU << " CDWcounter: " << cwcnt << " row: " << row << " Loopval: " << loopval << " realcharge: " << realcharge << " confDBv: " << mCdwVersion; LOG(info) << "NDIGITS: " << digits.size(); @@ -1470,15 +1491,13 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) if (ruIndex < 0) { continue; } - short int nL = 0; - for (int iL = 0; iL < 3; iL++) { - if (mActiveLinks[ruIndex][iL]) { - nL++; // count active links - } + short int nL = getNumberOfActiveLinks(mActiveLinks[ruIndex]); + if (isLocal) { + nL = ruIndex > 47 ? 2 : 3; } std::vector chipEnabled = getChipListFromRu(ruIndex, mActiveLinks[ruIndex]); // chip boundaries // Fill the chipDone info string - if (mRunTypeRUCopy[ruIndex] == nInjScaled * nL) { + if (mRunTypeRUCopy[ruIndex] == nInjScaled * nL && nL > 0) { for (short int iChip = 0; iChip < chipEnabled.size(); iChip++) { if ((chipEnabled[iChip] % mChipModBase) != mChipModSel) { continue; @@ -1488,14 +1507,37 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) mRunTypeRUCopy[ruIndex] = 0; // reset here is safer (the other counter is reset in finalize) } // Check if scan of a row is finished: only for specific scans! - bool passCondition = (mCdwCntRU[ruIndex][row] >= nInjScaled * nL); - if (mScanType == 'p' || mScanType == 't') { - passCondition = passCondition && (mLoopVal[ruIndex][row] == mMax); - if (mVerboseOutput) { - LOG(info) << "PassCondition: " << passCondition << " - (mCdwCntRU,mLoopVal) of RU" << ruIndex << " row " << row << " = (" << mCdwCntRU[ruIndex][row] << ", " << mLoopVal[ruIndex][row] << ")"; + bool passCondition = nL > 0 ? true : false; + for (int j1 = 0; j1 < N_RANGE2; j1++) { + for (int j2 = 0; j2 < N_RANGE; j2++) { + if (mScanType == 'D' || mScanType == 'A') { // D and A are processed in finalize and include >1 rows: row data can be mixed in time! + for (int ir = 0; ir < mRowScan; ir += mRowStep) { + if (!mCdwCntRU[ruIndex].count(ir)) { + passCondition = false; + break; + } else if (mCdwCntRU[ruIndex][ir][j1][j2] < nInjScaled * nL) { + passCondition = false; + break; + } + } + } else if (mScanType == 't') { // ToT scan is done in specific ranges depending on charge (see ITSComm) + if ((j1 == 0 && j2 < ((600 - mMin) / mStep)) || (j2 >= ((600 - mMin) / mStep) && j2 <= ((800 - mMin) / mStep)) || (j1 == 1 && j2 > ((800 - mMin) / mStep))) { + if (mCdwCntRU[ruIndex][row][j1][j2] < nInjScaled * nL) { + passCondition = false; + break; + } + } + } else if (mCdwCntRU[ruIndex][row][j1][j2] < nInjScaled * nL) { + passCondition = false; + break; + } + } + if (!passCondition) { + break; } - } else if (mVerboseOutput) { - LOG(info) << "PassCondition: " << passCondition << " - mCdwCntRU of RU" << ruIndex << " row " << row << " = " << mCdwCntRU[ruIndex][row]; + } + if (mVerboseOutput) { + LOG(info) << "PassCondition: " << passCondition << " - mCdwCntRU of RU" << ruIndex << " row " << row << " = " << mCdwCntRU[ruIndex][row][0][0] << "(Links: " << mActiveLinks[ruIndex][0] << ", " << mActiveLinks[ruIndex][1] << "," << mActiveLinks[ruIndex][2] << ")"; } if (mScanType != 'D' && mScanType != 'A' && mScanType != 'P' && mScanType != 'R' && passCondition) { @@ -1509,32 +1551,48 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) if (mPixelHits.count(chipID)) { if (mPixelHits[chipID].count(row)) { // make sure the row exists extractAndUpdate(chipID, row); - if (mScanType != 'p' && (mScanType != 'r' || mLoopVal[ruIndex][row] == mMax)) { // do not erase for scantype = p because in finalize() we have calculate2Dparams + if (mScanType != 'p') { // do not erase for scantype = p because in finalize() we have calculate2Dparams mPixelHits[chipID].erase(row); } } } } } - mCdwCntRU[ruIndex][row] = 0; // reset + mCdwCntRU[ruIndex].erase(row); // row is gone + } + + if (mRunTypeRU[ruIndex] >= nInjScaled * nL && passCondition) { + mFlagsRU[ruIndex] = true; + finalize(); + // Reset Active Links, mRunTypeRU, mFlagsRU (needed only for local data replay!) + if (mVerboseOutput) { + LOG(info) << "Resetting links of RU " << ruIndex; + } + if (!isLocal) { + mActiveLinks[ruIndex][0] = 0; + mActiveLinks[ruIndex][1] = 0; + mActiveLinks[ruIndex][2] = 0; + mRunTypeRU[ruIndex] = 0; + mFlagsRU[ruIndex] = false; + mCdwCntRU.erase(ruIndex); // for D,A,P,R (not entering the if above) + } + + LOG(info) << "Shipping all outputs to aggregator (before endOfStream arrival!)"; + pc.outputs().snapshot(Output{"ITS", "TSTR", (unsigned int)mChipModSel}, this->mTuning); + pc.outputs().snapshot(Output{"ITS", "PIXTYP", (unsigned int)mChipModSel}, this->mPixStat); + pc.outputs().snapshot(Output{"ITS", "RUNT", (unsigned int)mChipModSel}, this->mRunType); + pc.outputs().snapshot(Output{"ITS", "SCANT", (unsigned int)mChipModSel}, this->mScanType); + pc.outputs().snapshot(Output{"ITS", "FITT", (unsigned int)mChipModSel}, this->mFitType); + pc.outputs().snapshot(Output{"ITS", "CONFDBV", (unsigned int)mChipModSel}, this->mConfDBv); + pc.outputs().snapshot(Output{"ITS", "QCSTR", (unsigned int)mChipModSel}, this->mChipDoneQc); + // reset the DCSconfigObject_t before next ship out + mTuning.clear(); + mPixStat.clear(); + mChipDoneQc.clear(); } } // for (ROFs) - if (!(this->mRunTypeUp)) { - finalize(); - LOG(info) << "Shipping all outputs to aggregator (before endOfStream arrival!)"; - pc.outputs().snapshot(Output{"ITS", "TSTR", (unsigned int)mChipModSel}, this->mTuning); - pc.outputs().snapshot(Output{"ITS", "PIXTYP", (unsigned int)mChipModSel}, this->mPixStat); - pc.outputs().snapshot(Output{"ITS", "RUNT", (unsigned int)mChipModSel}, this->mRunType); - pc.outputs().snapshot(Output{"ITS", "SCANT", (unsigned int)mChipModSel}, this->mScanType); - pc.outputs().snapshot(Output{"ITS", "FITT", (unsigned int)mChipModSel}, this->mFitType); - pc.outputs().snapshot(Output{"ITS", "CONFDBV", (unsigned int)mChipModSel}, this->mConfDBv); - pc.outputs().snapshot(Output{"ITS", "QCSTR", (unsigned int)mChipModSel}, this->mChipDoneQc); - // reset the DCSconfigObject_t before next ship out - mTuning.clear(); - mPixStat.clear(); - mChipDoneQc.clear(); - } else if (pc.transitionState() == TransitionHandlingState::Requested) { + if (pc.transitionState() == TransitionHandlingState::Requested) { LOG(info) << "Run stop requested during the scan, sending output to aggregator and then stopping to process new data"; mRunStopRequested = true; finalize(); // calculating average thresholds based on what's collected up to this moment @@ -1769,18 +1827,19 @@ void ITSThresholdCalibrator::finalize() if (mScanType == 'I') { // Only ITHR scan: assign default ITHR = 50 if chip has no avg ITHR for (auto& iRU : mRuSet) { - if (mRunTypeRU[iRU] >= nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU]) || mRunStopRequested) { + if (mFlagsRU[iRU] || mRunStopRequested) { std::vector chipList = getChipListFromRu(iRU, mActiveLinks[iRU]); for (size_t i = 0; i < chipList.size(); i++) { if ((chipList[i] % mChipModBase) != mChipModSel) { continue; } - if (!mThresholds.count(chipList[i])) { + if (!mThresholds.count(chipList[i]) && !isChipDB[chipList[i]]) { if (mVerboseOutput) { LOG(info) << "Setting ITHR = 50 for chip " << chipList[i]; } std::vector data = {50, 0, 0, 0, 0}; addDatabaseEntry(chipList[i], name, data, false); + isChipDB[chipList[i]] = true; } } } @@ -1790,7 +1849,7 @@ void ITSThresholdCalibrator::finalize() auto it = this->mThresholds.cbegin(); while (it != this->mThresholds.cend()) { short int iRU = getRUID(it->first); - if (!isCRUITS && (mRunTypeRU[iRU] < nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU]) && !mRunStopRequested)) { + if (!isCRUITS && (!mFlagsRU[iRU]) && !mRunStopRequested) { ++it; continue; } @@ -1805,7 +1864,7 @@ void ITSThresholdCalibrator::finalize() if (mVerboseOutput) { LOG(info) << "Average or mpv " << name << " of chip " << it->first << " = " << outVal << " e-"; } - float status = ((float)it->second[4] / (float)(it->second[4] + it->second[5])) * 100.; // percentage of successful threshold extractions + float status = ((float)it->second[4] / (float)(mRowScan * N_COL)) * 100.; // percentage of successful threshold extractions if (status < mPercentageCut && (mScanType == 'I' || mScanType == 'V')) { if (mScanType == 'I') { // default ITHR if percentage of success < mPercentageCut outVal = 50.; @@ -1822,6 +1881,7 @@ void ITSThresholdCalibrator::finalize() } std::vector data = {outVal, rmsT, avgN, rmsN, status}; this->addDatabaseEntry(it->first, name, data, false); + isChipDB[it->first] = true; it = this->mThresholds.erase(it); } } else if (this->mScanType == 'D' || this->mScanType == 'A') { @@ -1831,7 +1891,7 @@ void ITSThresholdCalibrator::finalize() auto itchip = this->mPixelHits.cbegin(); while (itchip != this->mPixelHits.cend()) { // loop over chips collected short int iRU = getRUID(itchip->first); - if (!isCRUITS && (mRunTypeRU[iRU] < nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU]) && !mRunStopRequested)) { + if (!isCRUITS && !mFlagsRU[iRU] && !mRunStopRequested) { ++itchip; continue; } @@ -1845,7 +1905,7 @@ void ITSThresholdCalibrator::finalize() if (this->mVerboseOutput) { LOG(info) << "Chip " << itchip->first << " hits extracted"; } - ++itchip; + itchip = mPixelHits.erase(itchip); } auto it = this->mNoisyPixID.cbegin(); @@ -1886,7 +1946,7 @@ void ITSThresholdCalibrator::finalize() auto itchip = this->mPixelHits.cbegin(); while (itchip != mPixelHits.cend()) { int iRU = getRUID(itchip->first); - if (!mRunStopRequested && mRunTypeRU[iRU] < nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU])) { + if (!mRunStopRequested && !mFlagsRU[iRU]) { ++itchip; continue; } @@ -1909,15 +1969,11 @@ void ITSThresholdCalibrator::finalize() if (this->mVerboseOutput) { LOG(info) << "Chip " << itchip->first << " hits extracted"; } - ++itchip; + itchip = mPixelHits.erase(itchip); } // reset RU counters so that the chips which are done will not appear again in the DCSConfigObject } - for (auto& ru : thisRUs) { - mRunTypeRU[ru] = 0; // reset - } - return; } @@ -1928,9 +1984,9 @@ void ITSThresholdCalibrator::endOfStream(EndOfStreamContext& ec) { if (!isEnded && !mRunStopRequested) { LOGF(info, "endOfStream report:", mSelfName); - if (isCRUITS) { - finalize(); - } + LOG(info) << "Calling finalize(), doing nothing if scan has properly ended, otherwise save partial data in ROOT trees as backup"; + finalize(); + this->finalizeOutput(); isEnded = true; } @@ -1943,6 +1999,8 @@ void ITSThresholdCalibrator::stop() { if (!isEnded) { LOGF(info, "stop() report:", mSelfName); + LOG(info) << "Calling finalize(), doing nothing if scan has properly ended, otherwise save partial data in ROOT trees as backup"; + finalize(); this->finalizeOutput(); isEnded = true; } @@ -1996,6 +2054,7 @@ DataProcessorSpec getITSThresholdCalibratorSpec(const ITSCalibInpConf& inpConf) {"manual-step2", VariantType::Int, 1, {"Step2 value: defines the steps between manual-min2 and manual-max2. Default is 1. Use only in manual mode"}}, {"manual-scantype", VariantType::String, "T", {"scan type, can be D, T, I, V, P, p: use only in manual mode"}}, {"manual-strobewindow", VariantType::Int, 5, {"strobe duration in clock cycles, default is 5 = 125 ns: use only in manual mode"}}, + {"manual-rowscan", VariantType::Int, 512, {"Number of ALPIDE rows scanned in the run: use only in manual mode"}}, {"save-tree", VariantType::Bool, false, {"Flag to save ROOT tree on disk: use only in manual mode"}}, {"scale-ninj", VariantType::Bool, false, {"Flag to activate the scale of the number of injects to be used to count hits from specific MEBs: use only in manual mode and in combination with --meb-select"}}, {"enable-mpv", VariantType::Bool, false, {"Flag to enable calculation of most-probable value in vcasn/ithr scans"}}, @@ -2009,7 +2068,8 @@ DataProcessorSpec getITSThresholdCalibratorSpec(const ITSCalibInpConf& inpConf) {"charge-b", VariantType::Int, 0, {"To use with --calculate-slope, it defines the charge (in DAC) for the 2nd point used for the slope calculation"}}, {"meb-select", VariantType::Int, -1, {"Select from which multi-event buffer consider the hits: 0,1 or 2"}}, {"s-curve-col-step", VariantType::Int, 8, {"save s-curves points to tree every s-curve-col-step pixels on 1 row"}}, - {"percentage-cut", VariantType::Int, 25, {"discard chip in ITHR/VCASN scan if the percentage of success is less than this cut"}}}}; + {"percentage-cut", VariantType::Int, 25, {"discard chip in ITHR/VCASN scan if the percentage of success is less than this cut"}}, + {"local-processing", VariantType::Bool, false, {"Enable in case of data replay of scans processed row by row or in 1 go in finalize() but with partial data in the raw TF (e.g. data dump stopped before the real end of run)"}}}}; } } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx index 5cb6aa199ab64..c10b4aa32f054 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx @@ -19,7 +19,8 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ReconstructionDataFormats/Vertex.h" +#include "ITStracking/Definitions.h" +#include "ITStracking/TrackingConfigParam.h" using namespace o2::framework; @@ -27,7 +28,6 @@ namespace o2 { namespace its { -using Vertex = o2::dataformats::Vertex>; template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; @@ -39,6 +39,7 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) { // Spectators for logging // this is only to restore the original behavior + const auto writeContLabels = VertexerParamConfig::Instance().outputContLabels && useMC; auto tracksSize = std::make_shared(0); auto tracksSizeGetter = [tracksSize](std::vector const& tracks) { *tracksSize = tracks.size(); @@ -69,6 +70,11 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) "ITSVertexMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled ""}, + BranchDefinition{InputSpec{"labelsVerticesContributors", "ITS", "VERTICESMCTRCONT", 0}, + "ITSVertexMCTruthCont", + (writeContLabels ? 1 : 0), // one branch if + // requested + ""}, BranchDefinition{InputSpec{"MC2ROframes", "ITS", "ITSTrackMC2ROF", 0}, "ITSTracksMC2ROF", (useMC ? 1 : 0), // one branch if mc labels enabled diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index 9f84ee6522567..12d84ca7ab6ad 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -15,20 +15,20 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "ITSWorkflow/TrackerSpec.h" +#include "ITStracking/Definitions.h" +#include "ITStracking/TrackingConfigParam.h" namespace o2 { using namespace framework; namespace its { -using Vertex = o2::dataformats::Vertex>; - TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, int trgType, - const TrackingMode& trMode, + const TrackingMode::Type trMode, const bool overrBeamEst, - o2::gpu::GPUDataTypes::DeviceType dType) : mGGCCDBRequest(gr), + o2::gpu::gpudatatypes::DeviceType dType) : mGGCCDBRequest(gr), mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)}, mITSTrackingInterface{isMC, trgType, overrBeamEst} { @@ -59,7 +59,7 @@ void TrackerDPL::run(ProcessingContext& pc) mITSTrackingInterface.updateTimeDependentParams(pc); mITSTrackingInterface.run(pc); mTimer.Stop(); - LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); + LOGP(info, "CPU Reconstruction time for this TF {:.2f} s (cpu), {:.2f} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); } void TrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -74,12 +74,11 @@ void TrackerDPL::endOfStream(EndOfStreamContext& ec) void TrackerDPL::end() { - mITSTrackingInterface.end(); mITSTrackingInterface.printSummary(); LOGF(info, "ITS CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, const std::string& trModeS, const bool overrBeamEst, o2::gpu::GPUDataTypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) { std::vector inputs; @@ -123,6 +122,9 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, const st outputs.emplace_back("ITS", "VERTICESMCPUR", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe); + if (VertexerParamConfig::Instance().outputContLabels) { + outputs.emplace_back("ITS", "VERTICESMCTRCONT", 0, Lifetime::Timeframe); + } } return DataProcessorSpec{ @@ -132,8 +134,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, const st AlgorithmSpec{adaptFromTask(ggRequest, useMC, trgType, - trModeS == "sync" ? o2::its::TrackingMode::Sync : trModeS == "async" ? o2::its::TrackingMode::Async - : o2::its::TrackingMode::Cosmics, + trMode, overrBeamEst, dType)}, Options{}}; diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx index 168e1363d6fb5..8080883888d40 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx @@ -11,7 +11,6 @@ #include "ITSWorkflow/RecoWorkflow.h" #include "CommonUtils/ConfigurableParam.h" -#include "ITStracking/TrackingConfigParam.h" #include "ITStracking/Configuration.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -42,10 +41,10 @@ void customize(std::vector& workflowOptions) {"clusters-from-upstream", o2::framework::VariantType::Bool, false, {"clusters will be provided from upstream, skip clusterizer"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, - {"trackerCA", o2::framework::VariantType::Bool, false, {"use trackerCA (default: trackerCM)"}}, + {"trackerCA", o2::framework::VariantType::Bool, false, {"use trackerCA (deprecated)"}}, // FIXME: keep this around to not break scripts {"ccdb-meanvertex-seed", o2::framework::VariantType::Bool, false, {"use MeanVertex from CCDB if available to provide beam position seed (default: false)"}}, {"select-with-triggers", o2::framework::VariantType::String, "none", {"use triggers to prescale processed ROFs: phys, trd, none"}}, - {"tracking-mode", o2::framework::VariantType::String, "sync", {"sync,async,cosmics"}}, + {"tracking-mode", o2::framework::VariantType::String, "sync", {"sync,async,cosmics,unset,off"}}, {"disable-tracking", o2::framework::VariantType::Bool, false, {"disable tracking step"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, @@ -65,19 +64,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); auto beamPosOVerride = configcontext.options().get("ccdb-meanvertex-seed"); - auto useCAtracker = configcontext.options().get("trackerCA"); auto trmode = configcontext.options().get("tracking-mode"); auto selTrig = configcontext.options().get("select-with-triggers"); auto useGpuWF = configcontext.options().get("use-gpu-workflow"); - auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); + auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); auto useGeom = configcontext.options().get("use-full-geometry"); if (configcontext.options().get("disable-tracking")) { - trmode = ""; + trmode = "off"; } - std::transform(trmode.begin(), trmode.end(), trmode.begin(), [](unsigned char c) { return std::tolower(c); }); o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); int trType = 0; @@ -91,8 +88,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } } auto wf = o2::its::reco_workflow::getWorkflow(useMC, - useCAtracker, - trmode, + o2::its::TrackingMode::fromString(trmode), beamPosOVerride, extDigits, extClusters, diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h index 503e8332c4cf5..20b5407d614c5 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h @@ -95,7 +95,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo Int_t getSensorIndex(Int_t half, Int_t disk, Int_t ladder, Int_t sensor) const; /// get layer index (0:9) from the chip index - Int_t getLayer(Int_t index) const; + Int_t getLayer(Int_t index) const final; /// This routine computes the half, disk, ladder and sensor number /// given the sensor index number @@ -122,7 +122,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo { return extractNumberOfDisks(half); } - /// Returns the number of halfs MFT + /// Returns the number of halves MFT Int_t getNumberOfHalfs() { return extractNumberOfHalves(); @@ -181,7 +181,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo Int_t extractVolumeCopy(const Char_t* name, const Char_t* prefix) const; /// Get the transformation matrix of the sensor [...] - /// for a given sensor 'index' by quering the TGeoManager + /// for a given sensor 'index' by querying the TGeoManager TGeoHMatrix* extractMatrixSensor(Int_t index) const; // Create matrix for transformation from sensor local frame to global one diff --git a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseCalibratorSpec.h b/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseCalibratorSpec.h index c28e5d82ca46f..dee8fa4531bed 100644 --- a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseCalibratorSpec.h +++ b/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseCalibratorSpec.h @@ -56,17 +56,17 @@ class NoiseCalibratorSpec : public Task void sendOutputCcdbMerge(DataAllocator& output); void sendOutputCcdbDcs(DataAllocator& output); void sendOutputDcs(DataAllocator& output); + void sendOutputDcsMerge(DataAllocator& output); void setOutputDcs(const o2::itsmft::NoiseMap& payload); o2::itsmft::NoiseMap mNoiseMap{936}; std::unique_ptr mCalibrator = nullptr; std::shared_ptr mCCDBRequest; std::string mPath; - std::string mPathMerge; + std::string mPathSingle; std::string mMeta; std::vector> mNoiseMapForDcs; std::string mPathDcs; - std::string mPathDcsMerge; std::string mOutputType; double mThresh; diff --git a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h b/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h deleted file mode 100644 index a8280467b14c9..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file NoiseSlotCalibrator.h - -#ifndef O2_MFT_NOISESLOTCALIBRATOR -#define O2_MFT_NOISESLOTCALIBRATOR - -#include - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" - -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/Digit.h" -#include "DataFormatsITSMFT/NoiseMap.h" -#include "gsl/span" - -namespace o2 -{ - -namespace itsmft -{ -class ROFRecord; -} // namespace itsmft - -namespace mft -{ - -class NoiseSlotCalibrator : public o2::calibration::TimeSlotCalibration -{ - using Slot = calibration::TimeSlot; - - public: - NoiseSlotCalibrator() { setUpdateAtTheEndOfRunOnly(); } - NoiseSlotCalibrator(float prob, float relErr) : mProbabilityThreshold(prob), mProbRelErr(relErr) - { - setUpdateAtTheEndOfRunOnly(); - setSlotLength(INFINITE_TF); - mMinROFs = 1.1 * o2::itsmft::NoiseMap::getMinROFs(prob, relErr); - LOGP(info, "At least {} ROFs needed to apply threshold {} with relative error {}", mMinROFs, mProbabilityThreshold, mProbRelErr); - } - ~NoiseSlotCalibrator() final = default; - - void setThreshold(unsigned int t) { mThreshold = t; } - - bool processTimeFrame(calibration::TFType tf, - gsl::span const& digits, - gsl::span const& rofs); - - bool processTimeFrame(calibration::TFType tf, - gsl::span const& clusters, - gsl::span const& patterns, - gsl::span const& rofs); - - void setMinROFs(long n) { mMinROFs = n; } - - void finalize() - { - LOG(info) << "Number of processed strobes is " << mNumberOfStrobes; - auto& slot = getSlots().back(); - slot.getContainer()->applyProbThreshold(mProbabilityThreshold, mNumberOfStrobes); - } - - const o2::itsmft::NoiseMap& getNoiseMap(long& start, long& end) - { - const auto& slot = getSlots().back(); - start = slot.getTFStart(); - end = slot.getTFEnd(); - return *(slot.getContainer()); - } - - // Functions overloaded from the calibration framework - bool process(calibration::TFType tf, const gsl::span data) final; - - // Functions required by the calibration framework - void initOutput() final {} - Slot& emplaceNewSlot(bool, calibration::TFType, calibration::TFType) final; - void finalizeSlot(Slot& slot) final; - bool hasEnoughData(const Slot& slot) const final; - - private: - float mProbabilityThreshold = 1e-6f; - float mProbRelErr = 0.2; // relative error on channel noise to apply the threshold - long mMinROFs = 0; - unsigned int mThreshold = 100; - unsigned int mNumberOfStrobes = 0; -}; - -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_NOISESLOTCALIBRATOR */ diff --git a/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx b/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx deleted file mode 100644 index b9e590cca0b63..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx +++ /dev/null @@ -1,1660 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -//----------------------------------------------------------------------------- -/// \file Alignment -/// Alignment class for the ALICE DiMuon spectrometer -/// -/// MUON specific alignment class which interface to AliMillepede. -/// For each track ProcessTrack calculates the local and global derivatives -/// at each cluster and fill the corresponding local equations. Provide methods -/// for fixing or constraining detection elements for best results. -/// -/// \author Javier Castillo Castellanos -//----------------------------------------------------------------------------- - -#include "MCHAlign/Alignment.h" -#include "MCHAlign/MillePede2.h" -#include "MCHAlign/MillePedeRecord.h" -#include - -#include "MCHTracking/Track.h" -#include "MCHTracking/TrackParam.h" -#include "MCHTracking/Cluster.h" -#include "TGeoManager.h" - -// #include "DataFormatsMCH/ROFRecord.h" -// #include "DataFormatsMCH/TrackMCH.h" -// #include "DataFormatsMCH/Cluster.h" -// #include "DataFormatsMCH/Digit.h" - -// #include "AliMUONGeometryTransformer.h" -// #include "AliMUONGeometryModuleTransformer.h" -// #include "MCHAlign/AliMUONGeometryDetElement.h" -// #include "AliMUONGeometryBuilder.h" -#include "MCHGeometryCreator/Geometry.h" -#include "MCHGeometryTest/Helpers.h" -#include "MCHGeometryTransformer/Transformations.h" -#include "TGeoManager.h" - -// #include "Align/Millepede2Record.h" //to be replaced -// #include "AliMpExMap.h" -// #include "AliMpExMapIterator.h" - -#include "DetectorsCommonDataFormats/AlignParam.h" -#include "Framework/Logger.h" - -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace mch -{ - -using namespace std; - -//_____________________________________________________________________ -// static variables -const Int_t Alignment::fgNDetElemCh[Alignment::fgNCh] = {4, 4, 4, 4, 18, 18, 26, 26, 26, 26}; -const Int_t Alignment::fgSNDetElemCh[Alignment::fgNCh + 1] = {0, 4, 8, 12, 16, 34, 52, 78, 104, 130, 156}; - -// number of detector elements in each half-chamber -const Int_t Alignment::fgNDetElemHalfCh[Alignment::fgNHalfCh] = {2, 2, 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 13}; - -// list of detector elements for each half chamber -const Int_t Alignment::fgDetElemHalfCh[Alignment::fgNHalfCh][Alignment::fgNDetHalfChMax] = - { - {100, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {101, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {200, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {201, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {300, 303, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {301, 302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {400, 403, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {401, 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {500, 501, 502, 503, 504, 514, 515, 516, 517, 0, 0, 0, 0}, - {505, 506, 507, 508, 509, 510, 511, 512, 513, 0, 0, 0, 0}, - - {600, 601, 602, 603, 604, 614, 615, 616, 617, 0, 0, 0, 0}, - {605, 606, 607, 608, 609, 610, 611, 612, 613, 0, 0, 0, 0}, - - {700, 701, 702, 703, 704, 705, 706, 720, 721, 722, 723, 724, 725}, - {707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719}, - - {800, 801, 802, 803, 804, 805, 806, 820, 821, 822, 823, 824, 825}, - {807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819}, - - {900, 901, 902, 903, 904, 905, 906, 920, 921, 922, 923, 924, 925}, - {907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919}, - - {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1020, 1021, 1022, 1023, 1024, 1025}, - {1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019} - -}; - -//_____________________________________________________________________ -/// self initialized array, used for adding constraints -class Array -{ - - public: - /// contructor - Array(void) - { - for (Int_t i = 0; i < Alignment::fNGlobal; ++i) { - values[i] = 0; - } - } - - /// array - Double_t values[Alignment::fNGlobal]; - - private: - /// Not implemented - Array(const Array&); - - /// Not implemented - Array& operator=(const Array&); -}; - -//________________________________________________________________________ -Double_t Square(Double_t x) { return x * x; } - -//_____________________________________________________________________ -Alignment::Alignment() - : TObject(), - fInitialized(kFALSE), - fRunNumber(0), - fBFieldOn(kFALSE), - fRefitStraightTracks(kFALSE), - fStartFac(256), - fResCutInitial(100), - fResCut(100), - fMillepede(0L), // to be modified - fCluster(0L), - fNStdDev(3), - fDetElemNumber(0), - fTrackRecord(), - fTransformCreator(), - fGeoCombiTransInverse(), - fDoEvaluation(kFALSE), - fTrackParamOrig(0), - fTrackParamNew(0), - fTFile(0), - fTTree(0) -{ - /// constructor - fSigma[0] = 1.5e-1; - fSigma[1] = 1.0e-2; - - // default allowed variations - fAllowVar[0] = 0.5; // x - fAllowVar[1] = 0.5; // y - fAllowVar[2] = 0.01; // phi_z - fAllowVar[3] = 5; // z - - // initialize millepede - fMillepede = new MillePede2(); - // fMillepede = new o2::align::Mille("theMilleFile.txt"); // To be replaced by MillePede2 - - // initialize degrees of freedom - // by default all parameters are free - for (Int_t iPar = 0; iPar < fNGlobal; ++iPar) { - fGlobalParameterStatus[iPar] = kFreeParId; - } - - // initialize local equations - for (int i = 0; i < fNLocal; ++i) { - fLocalDerivatives[i] = 0.0; - } - - for (int i = 0; i < fNGlobal; ++i) { - fGlobalDerivatives[i] = 0.0; - } -} - -//_____________________________________________________________________ -// Alignment::~Alignment() -//{ -// /// destructor -//} -// Alignment::~Alignment() = default; -//_____________________________________________________________________ -void Alignment::init(void) -{ - - /// initialize - /** - initialize millipede - must be called after necessary detectors have been fixed, - but before constrains are added and before global parameters initial value are set - */ - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - // assign proper groupID to free parameters - Int_t nGlobal = 0; - for (Int_t iPar = 0; iPar < fNGlobal; ++iPar) { - - if (fGlobalParameterStatus[iPar] == kFixedParId) { - // fixed parameters are left unchanged - continue; - - } else if (fGlobalParameterStatus[iPar] == kFreeParId || fGlobalParameterStatus[iPar] == kGroupBaseId) { - - // free parameters or first element of group are assigned a new group id - fGlobalParameterStatus[iPar] = nGlobal++; - continue; - - } else if (fGlobalParameterStatus[iPar] < kGroupBaseId) { - - // get detector element id from status, get chamber parameter id - const Int_t iDeBase(kGroupBaseId - 1 - fGlobalParameterStatus[iPar]); - const Int_t iParBase = iPar % fgNParCh; - - // check - if (iDeBase < 0 || iDeBase >= iPar / fgNParCh) { - LOG(fatal) << "Group for parameter index " << iPar << " has wrong base detector element: " << iDeBase; - } - - // assign identical group id to current - fGlobalParameterStatus[iPar] = fGlobalParameterStatus[iDeBase * fgNParCh + iParBase]; - LOG(info) << "Parameter " << iPar << " grouped to detector " << iDeBase << " (" << GetParameterMaskString(1 << iParBase).Data() << ")"; - - } else - LOG(fatal) << "Unrecognized parameter status for index " << iPar << ": " << fGlobalParameterStatus[iPar]; - } - - LOG(info) << "Free Parameters: " << nGlobal << " out of " << fNGlobal; - - // initialize millepede - // fMillepede->InitMille(fNGlobal, fNLocal, fNStdDev, fResCut, fResCutInitial, fGlobalParameterStatus); - fMillepede->InitMille(fNGlobal, fNLocal, fNStdDev, fResCut, fResCutInitial); // MillePede2 implementation - - fInitialized = kTRUE; - - // some debug output - for (Int_t iPar = 0; iPar < fgNParCh; ++iPar) { - LOG(info) << "fAllowVar[" << iPar << "]= " << fAllowVar[iPar]; - } - - // set allowed variations for all parameters - for (Int_t iDet = 0; iDet < fgNDetElem; ++iDet) { - for (Int_t iPar = 0; iPar < fgNParCh; ++iPar) { - fMillepede->SetParSigma(iDet * fgNParCh + iPar, fAllowVar[iPar]); - } - } - - // Set iterations - if (fStartFac > 1) { - fMillepede->SetIterations(fStartFac); - } - // setup monitoring TFile - if (fDoEvaluation && fRefitStraightTracks) { - fTFile = new TFile("Alignment.root", "RECREATE"); - fTTree = new TTree("TreeE", "Evaluation"); - - const Int_t kSplitlevel = 98; - const Int_t kBufsize = 32000; - - fTrackParamOrig = new LocalTrackParam(); - fTTree->Branch("fTrackParamOrig", "LocalTrackParam", &fTrackParamOrig, kBufsize, kSplitlevel); - - fTrackParamNew = new LocalTrackParam(); - fTTree->Branch("fTrackParamNew", "LocalTrackParam", &fTrackParamNew, kBufsize, kSplitlevel); - } -} - -//_____________________________________________________ -void Alignment::terminate(void) -{ - LOG(info) << "Closing Evaluation TFile"; - if (fTFile && fTTree) { - fTFile->cd(); - fTTree->Write(); - fTFile->Close(); - } -} - -//_____________________________________________________ -MillePedeRecord* Alignment::ProcessTrack(Track& track, Bool_t doAlignment, Double_t weight) -{ - /// process track for alignment minimization - /** - returns the alignment records for this track. - They can be stored in some output for later reprocessing. - */ - - // reset track records - fTrackRecord.Reset(); - if (fMillepede->GetRecord()) { - fMillepede->GetRecord()->Reset(); - } - - // loop over clusters to get starting values - Bool_t first(kTRUE); - // if (!trackParam) - // continue; - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get cluster - const Cluster* Cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // for first valid cluster, save track position as "starting" values - if (first) { - - first = kFALSE; - FillTrackParamData(&*itTrackParam); - fTrackPos0[0] = fTrackPos[0]; - fTrackPos0[1] = fTrackPos[1]; - fTrackPos0[2] = fTrackPos[2]; - fTrackSlope0[0] = fTrackSlope[0]; - fTrackSlope0[1] = fTrackSlope[1]; - - break; - } - } - - // redo straight track fit - if (fRefitStraightTracks) { - - // refit straight track - const LocalTrackParam trackParam(RefitStraightTrack(track, fTrackPos0[2])); - - // fill evaluation tree - if (fTrackParamOrig) { - fTrackParamOrig->fTrackX = fTrackPos0[0]; - fTrackParamOrig->fTrackY = fTrackPos0[1]; - fTrackParamOrig->fTrackZ = fTrackPos0[2]; - fTrackParamOrig->fTrackSlopeX = fTrackSlope[0]; - fTrackParamOrig->fTrackSlopeY = fTrackSlope[1]; - } - - // new ones - if (fTrackParamNew) { - fTrackParamNew->fTrackX = trackParam.fTrackX; - fTrackParamNew->fTrackY = trackParam.fTrackY; - fTrackParamNew->fTrackZ = trackParam.fTrackZ; - fTrackParamNew->fTrackSlopeX = trackParam.fTrackSlopeX; - fTrackParamNew->fTrackSlopeY = trackParam.fTrackSlopeY; - } - - if (fTTree) - fTTree->Fill(); - - /* - copy new parameters to stored ones for derivatives calculation - this is done only if BFieldOn is false, for which these parameters are used - */ - if (!fBFieldOn) { - fTrackPos0[0] = trackParam.fTrackX; - fTrackPos0[1] = trackParam.fTrackY; - fTrackPos0[2] = trackParam.fTrackZ; - fTrackSlope[0] = trackParam.fTrackSlopeX; - fTrackSlope[1] = trackParam.fTrackSlopeY; - } - } - - // second loop to perform alignment - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get track parameters - if (!&*itTrackParam) - continue; - - // get cluster - const Cluster* cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // fill local variables for this position --> one measurement - FillDetElemData(cluster); - FillRecPointData(cluster); - FillTrackParamData(&*itTrackParam); - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // calculate measurements - if (fBFieldOn) { - - // use residuals (cluster - track) for measurement - fMeas[0] = r[0] * (fClustPos[0] - fTrackPos[0]) + r[1] * (fClustPos[1] - fTrackPos[1]); - fMeas[1] = r[3] * (fClustPos[0] - fTrackPos[0]) + r[4] * (fClustPos[1] - fTrackPos[1]); - - } else { - - // use cluster position for measurement - fMeas[0] = (r[0] * fClustPos[0] + r[1] * fClustPos[1]); - fMeas[1] = (r[3] * fClustPos[0] + r[4] * fClustPos[1]); - } - - // Set local equations - LocalEquationX(); - LocalEquationY(); - } - - // copy track record - fMillepede->SetRecordRun(fRunNumber); - fMillepede->SetRecordWeight(weight); - fTrackRecord = *fMillepede->GetRecord(); - - // save record data - if (doAlignment) { - fMillepede->SaveRecordData(); - fMillepede->CloseDataRecStorage(); - } - - // return record - return &fTrackRecord; -} - -//______________________________________________________________________________ -void Alignment::ProcessTrack(MillePedeRecord* trackRecord) -{ - LOG(fatal) << __PRETTY_FUNCTION__ << " is disabled"; - - /// process track record - if (!trackRecord) - return; - - // // make sure record storage is initialized - if (!fMillepede->GetRecord()) { - fMillepede->InitDataRecStorage(kFalse); - } - // // copy content - *fMillepede->GetRecord() = *trackRecord; - - // save record - fMillepede->SaveRecordData(); - // write to local file - fMillepede->CloseDataRecStorage(); - - return; -} - -//_____________________________________________________________________ -void Alignment::FixAll(UInt_t mask) -{ - /// fix parameters matching mask, for all chambers - LOG(info) << "Fixing " << GetParameterMaskString(mask).Data() << " for all detector elements"; - - // fix all stations - for (Int_t i = 0; i < fgNDetElem; ++i) { - if (mask & ParX) - FixParameter(i, 0); - if (mask & ParY) - FixParameter(i, 1); - if (mask & ParTZ) - FixParameter(i, 2); - if (mask & ParZ) - FixParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::FixChamber(Int_t iCh, UInt_t mask) -{ - /// fix parameters matching mask, for all detector elements in a given chamber, counting from 1 - - // check boundaries - if (iCh < 1 || iCh > 10) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - // get first and last element - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - LOG(info) << "Fixing " << GetParameterMaskString(mask).Data() << " for detector element " << i; - - if (mask & ParX) - FixParameter(i, 0); - if (mask & ParY) - FixParameter(i, 1); - if (mask & ParTZ) - FixParameter(i, 2); - if (mask & ParZ) - FixParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::FixDetElem(Int_t iDetElemId, UInt_t mask) -{ - /// fix parameters matching mask, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - FixParameter(iDet, 0); - if (mask & ParY) - FixParameter(iDet, 1); - if (mask & ParTZ) - FixParameter(iDet, 2); - if (mask & ParZ) - FixParameter(iDet, 3); -} - -//_____________________________________________________________________ -void Alignment::FixHalfSpectrometer(const Bool_t* lChOnOff, UInt_t sidesMask, UInt_t mask) -{ - - /// Fix parameters matching mask for all detectors in selected chambers and selected sides of the spectrometer - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - if (!lChOnOff[iCh - 1]) - continue; - - // get detector element in chamber - Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - - // skip detector if its side is off - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber == 1 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber == 2 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber == 3 && !(sidesMask & SideBottomRight)) - continue; - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 5 && lDetElemNumber <= 10 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 11 && lDetElemNumber <= 13 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(sidesMask & SideBottomRight)) - continue; - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(sidesMask & SideBottomRight)) - continue; - } - - // detector is accepted, fix it - FixDetElem(i, mask); - } -} - -//______________________________________________________________________ -void Alignment::FixParameter(Int_t iPar) -{ - - /// fix a given parameter, counting from 0 - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - fGlobalParameterStatus[iPar] = kFixedParId; -} - -//_____________________________________________________________________ -void Alignment::ReleaseChamber(Int_t iCh, UInt_t mask) -{ - /// release parameters matching mask, for all detector elements in a given chamber, counting from 1 - - // check boundaries - if (iCh < 1 || iCh > 10) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - // get first and last element - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - LOG(info) << "Releasing " << GetParameterMaskString(mask).Data() << " for detector element " << i; - - if (mask & ParX) - ReleaseParameter(i, 0); - if (mask & ParY) - ReleaseParameter(i, 1); - if (mask & ParTZ) - ReleaseParameter(i, 2); - if (mask & ParZ) - ReleaseParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::ReleaseDetElem(Int_t iDetElemId, UInt_t mask) -{ - /// release parameters matching mask, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - ReleaseParameter(iDet, 0); - if (mask & ParY) - ReleaseParameter(iDet, 1); - if (mask & ParTZ) - ReleaseParameter(iDet, 2); - if (mask & ParZ) - ReleaseParameter(iDet, 3); -} - -//______________________________________________________________________ -void Alignment::ReleaseParameter(Int_t iPar) -{ - - /// release a given parameter, counting from 0 - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - fGlobalParameterStatus[iPar] = kFreeParId; -} - -//_____________________________________________________________________ -void Alignment::GroupChamber(Int_t iCh, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in a given chamber, counting from 1 - if (iCh < 1 || iCh > fgNCh) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - const Int_t detElemMin = 100 * iCh; - const Int_t detElemMax = 100 * iCh + fgNDetElemCh[iCh] - 1; - GroupDetElems(detElemMin, detElemMax, mask); -} - -//_____________________________________________________________________ -void Alignment::GroupHalfChamber(Int_t iCh, Int_t iHalf, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in a given tracking module (= half chamber), counting from 0 - if (iCh < 1 || iCh > fgNCh) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - if (iHalf < 0 || iHalf > 1) { - LOG(fatal) << "Invalid half chamber index " << iHalf; - } - - const Int_t iHalfCh = 2 * (iCh - 1) + iHalf; - GroupDetElems(&fgDetElemHalfCh[iHalfCh][0], fgNDetElemHalfCh[iHalfCh], mask); -} - -//_____________________________________________________________________ -void Alignment::GroupDetElems(Int_t detElemMin, Int_t detElemMax, UInt_t mask) -{ - /// group parameters matching mask for all detector elements between min and max - // check number of detector elements - const Int_t nDetElem = detElemMax - detElemMin + 1; - if (nDetElem < 2) { - LOG(fatal) << "Requested group of DEs " << detElemMin << "-" << detElemMax << " contains less than 2 DE's"; - } - - // create list - Int_t* detElemList = new int[nDetElem]; - for (Int_t i = 0; i < nDetElem; ++i) { - detElemList[i] = detElemMin + i; - } - - // group - GroupDetElems(detElemList, nDetElem, mask); - delete[] detElemList; -} - -//_____________________________________________________________________ -void Alignment::GroupDetElems(const Int_t* detElemList, Int_t nDetElem, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in list - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - const Int_t iDeBase(GetDetElemNumber(detElemList[0])); - for (Int_t i = 0; i < nDetElem; ++i) { - const Int_t iDeCurrent(GetDetElemNumber(detElemList[i])); - if (mask & ParX) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 0] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParY) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 1] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParTZ) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 2] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParZ) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 3] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - - if (i == 0) - LOG(info) << "Creating new group for detector " << detElemList[i] << " and variable " << GetParameterMaskString(mask).Data(); - else - LOG(info) << "Adding detector element " << detElemList[i] << " to current group"; - } -} - -//______________________________________________________________________ -void Alignment::SetChamberNonLinear(Int_t iCh, UInt_t mask) -{ - /// Set parameters matching mask as non linear, for all detector elements in a given chamber, counting from 1 - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - if (mask & ParX) - SetParameterNonLinear(i, 0); - if (mask & ParY) - SetParameterNonLinear(i, 1); - if (mask & ParTZ) - SetParameterNonLinear(i, 2); - if (mask & ParZ) - SetParameterNonLinear(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::SetDetElemNonLinear(Int_t iDetElemId, UInt_t mask) -{ - /// Set parameters matching mask as non linear, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - SetParameterNonLinear(iDet, 0); - if (mask & ParY) - SetParameterNonLinear(iDet, 1); - if (mask & ParTZ) - SetParameterNonLinear(iDet, 2); - if (mask & ParZ) - SetParameterNonLinear(iDet, 3); -} - -//______________________________________________________________________ -void Alignment::SetParameterNonLinear(Int_t iPar) -{ - /// Set nonlinear flag for parameter iPar - if (!fInitialized) { - LOG(fatal) << "Millepede not initialized"; - } - - fMillepede->SetNonLinear(iPar); - LOG(info) << "Parameter " << iPar << " set to non linear "; -} - -//______________________________________________________________________ -void Alignment::AddConstraints(const Bool_t* lChOnOff, UInt_t mask) -{ - /// Add constraint equations for selected chambers and degrees of freedom - - Array fConstraintX; - Array fConstraintY; - Array fConstraintTZ; - Array fConstraintZ; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - if (lChOnOff[iCh - 1]) { - - if (mask & ParX) - fConstraintX.values[i * fgNParCh + 0] = 1.0; - if (mask & ParY) - fConstraintY.values[i * fgNParCh + 1] = 1.0; - if (mask & ParTZ) - fConstraintTZ.values[i * fgNParCh + 2] = 1.0; - if (mask & ParZ) - fConstraintTZ.values[i * fgNParCh + 3] = 1.0; - } - } - - if (mask & ParX) - AddConstraint(fConstraintX.values, 0.0); - if (mask & ParY) - AddConstraint(fConstraintY.values, 0.0); - if (mask & ParTZ) - AddConstraint(fConstraintTZ.values, 0.0); - if (mask & ParZ) - AddConstraint(fConstraintZ.values, 0.0); -} - -//______________________________________________________________________ -void Alignment::AddConstraints(const Bool_t* lChOnOff, const Bool_t* lVarXYT, UInt_t sidesMask) -{ - /* - questions: - - is there not redundancy/inconsistency between lDetTLBR and lSpecLROnOff ? shouldn't we use only lDetTLBR ? - - why is weight ignored for ConstrainT and ConstrainB - - why is there no constrain on z - */ - - /// Add constraint equations for selected chambers, degrees of freedom and detector half - Double_t lMeanY = 0.; - Double_t lSigmaY = 0.; - Double_t lMeanZ = 0.; - Double_t lSigmaZ = 0.; - Int_t lNDetElem = 0; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - - // skip detector if chamber is off - if (lChOnOff[iCh - 1]) - continue; - - // get detector element id from detector element number - const Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - const Int_t lDetElemId = iCh * 100 + lDetElemNumber; - - // skip detector if its side is off - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber == 1 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber == 2 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber == 3 && !(sidesMask & SideBottomRight)) - continue; - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 5 && lDetElemNumber <= 10 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 11 && lDetElemNumber <= 13 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(sidesMask & SideBottomRight)) - continue; - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(sidesMask & SideBottomRight)) - continue; - } - - // get global x, y and z position - Double_t lDetElemGloX = 0.; - Double_t lDetElemGloY = 0.; - Double_t lDetElemGloZ = 0.; - - auto fTransform = fTransformCreator(lDetElemId); - o2::math_utils::Point3D SlatPos{0.0, 0.0, 0.0}; - o2::math_utils::Point3D GlobalPos; - - fTransform.LocalToMaster(SlatPos, GlobalPos); - lDetElemGloX = GlobalPos.x(); - lDetElemGloY = GlobalPos.y(); - lDetElemGloZ = GlobalPos.z(); - // fTransform->Local2Global(lDetElemId, 0, 0, 0, lDetElemGloX, lDetElemGloY, lDetElemGloZ); - - // increment mean Y, mean Z, sigmas and number of accepted detectors - lMeanY += lDetElemGloY; - lSigmaY += lDetElemGloY * lDetElemGloY; - lMeanZ += lDetElemGloZ; - lSigmaZ += lDetElemGloZ * lDetElemGloZ; - lNDetElem++; - } - - // calculate mean values - lMeanY /= lNDetElem; - lSigmaY /= lNDetElem; - lSigmaY = TMath::Sqrt(lSigmaY - lMeanY * lMeanY); - lMeanZ /= lNDetElem; - lSigmaZ /= lNDetElem; - lSigmaZ = TMath::Sqrt(lSigmaZ - lMeanZ * lMeanZ); - LOG(info) << "Used " << lNDetElem << " DetElem, MeanZ= " << lMeanZ << ", SigmaZ= " << lSigmaZ; - - // create all possible arrays - Array fConstraintX[4]; // Array for constraint equation X - Array fConstraintY[4]; // Array for constraint equation Y - Array fConstraintP[4]; // Array for constraint equation P - Array fConstraintXZ[4]; // Array for constraint equation X vs Z - Array fConstraintYZ[4]; // Array for constraint equation Y vs Z - Array fConstraintPZ[4]; // Array for constraint equation P vs Z - - // do we really need these ? - Array fConstraintXY[4]; // Array for constraint equation X vs Y - Array fConstraintYY[4]; // Array for constraint equation Y vs Y - Array fConstraintPY[4]; // Array for constraint equation P vs Y - - // fill Bool_t sides array based on masks, for convenience - Bool_t lDetTLBR[4]; - lDetTLBR[0] = sidesMask & SideTop; - lDetTLBR[1] = sidesMask & SideLeft; - lDetTLBR[2] = sidesMask & SideBottom; - lDetTLBR[3] = sidesMask & SideRight; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - - // skip detector if chamber is off - if (!lChOnOff[iCh - 1]) - continue; - - // get detector element id from detector element number - const Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - const Int_t lDetElemId = iCh * 100 + lDetElemNumber; - - // get global x, y and z position - Double_t lDetElemGloX = 0.; - Double_t lDetElemGloY = 0.; - Double_t lDetElemGloZ = 0.; - - auto fTransform = fTransformCreator(lDetElemId); - o2::math_utils::Point3D SlatPos{0.0, 0.0, 0.0}; - o2::math_utils::Point3D GlobalPos; - - fTransform.LocalToMaster(SlatPos, GlobalPos); - lDetElemGloX = GlobalPos.x(); - lDetElemGloY = GlobalPos.y(); - lDetElemGloZ = GlobalPos.z(); - // fTransform->Local2Global(lDetElemId, 0, 0, 0, lDetElemGloX, lDetElemGloY, lDetElemGloZ); - - // loop over sides - for (Int_t iSide = 0; iSide < 4; iSide++) { - - // skip if side is not selected - if (!lDetTLBR[iSide]) - continue; - - // skip detector if it is not in the selected side - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber == 1 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber == 2 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber == 3 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber >= 5 && lDetElemNumber <= 9 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber >= 10 && lDetElemNumber <= 13 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // constrain x - if (lVarXYT[0]) - fConstraintX[iSide].values[i * fgNParCh + 0] = 1; - - // constrain y - if (lVarXYT[1]) - fConstraintY[iSide].values[i * fgNParCh + 1] = 1; - - // constrain phi (rotation around z) - if (lVarXYT[2]) - fConstraintP[iSide].values[i * fgNParCh + 2] = 1; - - // x-z shearing - if (lVarXYT[3]) - fConstraintXZ[iSide].values[i * fgNParCh + 0] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // y-z shearing - if (lVarXYT[4]) - fConstraintYZ[iSide].values[i * fgNParCh + 1] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // phi-z shearing - if (lVarXYT[5]) - fConstraintPZ[iSide].values[i * fgNParCh + 2] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // x-y shearing - if (lVarXYT[6]) - fConstraintXY[iSide].values[i * fgNParCh + 0] = (lDetElemGloY - lMeanY) / lSigmaY; - - // y-y shearing - if (lVarXYT[7]) - fConstraintYY[iSide].values[i * fgNParCh + 1] = (lDetElemGloY - lMeanY) / lSigmaY; - - // phi-y shearing - if (lVarXYT[8]) - fConstraintPY[iSide].values[i * fgNParCh + 2] = (lDetElemGloY - lMeanY) / lSigmaY; - } - } - - // pass constraints to millepede - for (Int_t iSide = 0; iSide < 4; iSide++) { - // skip if side is not selected - if (!lDetTLBR[iSide]) - continue; - - if (lVarXYT[0]) - AddConstraint(fConstraintX[iSide].values, 0.0); - if (lVarXYT[1]) - AddConstraint(fConstraintY[iSide].values, 0.0); - if (lVarXYT[2]) - AddConstraint(fConstraintP[iSide].values, 0.0); - if (lVarXYT[3]) - AddConstraint(fConstraintXZ[iSide].values, 0.0); - if (lVarXYT[4]) - AddConstraint(fConstraintYZ[iSide].values, 0.0); - if (lVarXYT[5]) - AddConstraint(fConstraintPZ[iSide].values, 0.0); - if (lVarXYT[6]) - AddConstraint(fConstraintXY[iSide].values, 0.0); - if (lVarXYT[7]) - AddConstraint(fConstraintYY[iSide].values, 0.0); - if (lVarXYT[8]) - AddConstraint(fConstraintPY[iSide].values, 0.0); - } -} - -//______________________________________________________________________ -void Alignment::InitGlobalParameters(Double_t* par) -{ - /// Initialize global parameters with par array - if (!fInitialized) { - LOG(fatal) << "Millepede is not initialized"; - } - - fMillepede->SetGlobalParameters(par); -} - -//______________________________________________________________________ -void Alignment::SetAllowedVariation(Int_t iPar, Double_t value) -{ - /// "Encouraged" variation for degrees of freedom - // check initialization - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - // check initialization - if (!(iPar >= 0 && iPar < fgNParCh)) { - LOG(fatal) << "Invalid index: " << iPar; - } - - fAllowVar[iPar] = value; -} - -//______________________________________________________________________ -void Alignment::SetSigmaXY(Double_t sigmaX, Double_t sigmaY) -{ - - /// Set expected measurement resolution - fSigma[0] = sigmaX; - fSigma[1] = sigmaY; - - // print - for (Int_t i = 0; i < 2; ++i) { - LOG(info) << "fSigma[" << i << "] =" << fSigma[i]; - } -} - -//_____________________________________________________ -void Alignment::GlobalFit(Double_t* parameters, Double_t* errors, Double_t* pulls) -{ - - /// Call global fit; Global parameters are stored in parameters - fMillepede->GlobalFit(parameters, errors, pulls); - - LOG(info) << "Done fitting global parameters"; - for (int iDet = 0; iDet < fgNDetElem; ++iDet) { - LOG(info) << iDet << " " << parameters[iDet * fgNParCh + 0] << " " << parameters[iDet * fgNParCh + 1] << " " << parameters[iDet * fgNParCh + 3] << " " << parameters[iDet * fgNParCh + 2]; - } -} - -//_____________________________________________________ -void Alignment::PrintGlobalParameters() const -{ - fMillepede->PrintGlobalParameters(); -} - -//_____________________________________________________ -Double_t Alignment::GetParError(Int_t iPar) const -{ - return fMillepede->GetParError(iPar); -} - -// //______________________________________________________________________ -// AliMUONGeometryTransformer* Alignment::ReAlign( -// const AliMUONGeometryTransformer* transformer, -// const double* misAlignments, Bool_t) -// { - -// /// Returns a new AliMUONGeometryTransformer with the found misalignments -// /// applied. - -// // Takes the internal geometry module transformers, copies them -// // and gets the Detection Elements from them. -// // Takes misalignment parameters and applies these -// // to the local transform of the Detection Element -// // Obtains the global transform by multiplying the module transformer -// // transformation with the local transformation -// // Applies the global transform to a new detection element -// // Adds the new detection element to a new module transformer -// // Adds the new module transformer to a new geometry transformer -// // Returns the new geometry transformer - -// Double_t lModuleMisAlignment[fgNParCh] = {0}; -// Double_t lDetElemMisAlignment[fgNParCh] = {0}; -// const TClonesArray* oldMisAlignArray(transformer->GetMisAlignmentData()); - -// AliMUONGeometryTransformer* newGeometryTransformer = new AliMUONGeometryTransformer(); -// for (Int_t iMt = 0; iMt < transformer->GetNofModuleTransformers(); ++iMt) { - -// // module transformers -// const AliMUONGeometryModuleTransformer* kModuleTransformer = transformer->GetModuleTransformer(iMt, kTRUE); - -// AliMUONGeometryModuleTransformer* newModuleTransformer = new AliMUONGeometryModuleTransformer(iMt); -// newGeometryTransformer->AddModuleTransformer(newModuleTransformer); - -// // get transformation -// TGeoHMatrix deltaModuleTransform(DeltaTransform(lModuleMisAlignment)); - -// // update module -// TGeoHMatrix moduleTransform(*kModuleTransformer->GetTransformation()); -// TGeoHMatrix newModuleTransform(AliMUONGeometryBuilder::Multiply(deltaModuleTransform, moduleTransform)); -// newModuleTransformer->SetTransformation(newModuleTransform); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const Int_t moduleId(kModuleTransformer->GetModuleId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, moduleId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// deltaModuleTransform.Multiply(&oldMatrix); -// } -// } - -// // Create module mis alignment matrix -// newGeometryTransformer->AddMisAlignModule(kModuleTransformer->GetModuleId(), deltaModuleTransform); - -// AliMpExMap* detElements = kModuleTransformer->GetDetElementStore(); - -// TIter next(detElements->CreateIterator()); -// AliMUONGeometryDetElement* detElement; -// Int_t iDe(-1); -// while ((detElement = static_cast(next()))) { -// ++iDe; -// // make a new detection element -// AliMUONGeometryDetElement* newDetElement = new AliMUONGeometryDetElement(detElement->GetId(), detElement->GetVolumePath()); -// TString lDetElemName(detElement->GetDEName()); -// lDetElemName.ReplaceAll("DE", ""); - -// // store detector element id and number -// const Int_t iDetElemId = lDetElemName.Atoi(); -// if (DetElemIsValid(iDetElemId)) { - -// const Int_t iDetElemNumber(GetDetElemNumber(iDetElemId)); - -// for (int i = 0; i < fgNParCh; ++i) { -// lDetElemMisAlignment[i] = 0.0; -// if (iMt < fgNTrkMod) { -// lDetElemMisAlignment[i] = misAlignments[iDetElemNumber * fgNParCh + i]; -// } -// } - -// // get transformation -// TGeoHMatrix deltaGlobalTransform(DeltaTransform(lDetElemMisAlignment)); - -// // update module -// TGeoHMatrix globalTransform(*detElement->GetGlobalTransformation()); -// TGeoHMatrix newGlobalTransform(AliMUONGeometryBuilder::Multiply(deltaGlobalTransform, globalTransform)); -// newDetElement->SetGlobalTransformation(newGlobalTransform); -// newModuleTransformer->GetDetElementStore()->Add(newDetElement->GetId(), newDetElement); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const int detElemId(detElement->GetId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, detElemId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// deltaGlobalTransform.Multiply(&oldMatrix); -// } -// } - -// // Create misalignment matrix -// newGeometryTransformer->AddMisAlignDetElement(detElement->GetId(), deltaGlobalTransform); - -// } else { - -// // "invalid" detector elements come from MTR and are left unchanged -// Aliinfo(Form("Keeping detElement %i unchanged", iDetElemId)); - -// // update module -// TGeoHMatrix globalTransform(*detElement->GetGlobalTransformation()); -// newDetElement->SetGlobalTransformation(globalTransform); -// newModuleTransformer->GetDetElementStore()->Add(newDetElement->GetId(), newDetElement); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const int detElemId(detElement->GetId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, detElemId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// newGeometryTransformer->AddMisAlignDetElement(detElement->GetId(), oldMatrix); -// } -// } -// } -// } - -// newGeometryTransformer->AddModuleTransformer(newModuleTransformer); -// } - -// return newGeometryTransformer; -// } - -//______________________________________________________________________ -void Alignment::SetAlignmentResolution(const TClonesArray* misAlignArray, Int_t rChId, Double_t chResX, Double_t chResY, Double_t deResX, Double_t deResY) -{ - - /// Set alignment resolution to misalign objects to be stored in CDB - /// if rChId is > 0 set parameters for this chamber only, counting from 1 - TMatrixDSym mChCorrMatrix(6); - mChCorrMatrix[0][0] = chResX * chResX; - mChCorrMatrix[1][1] = chResY * chResY; - - TMatrixDSym mDECorrMatrix(6); - mDECorrMatrix[0][0] = deResX * deResX; - mDECorrMatrix[1][1] = deResY * deResY; - - o2::detectors::AlignParam* alignMat = 0x0; - - for (Int_t chId = 0; chId <= 9; ++chId) { - - // skip chamber if selection is valid, and does not match - if (rChId > 0 && chId + 1 != rChId) - continue; - - TString chName1; - TString chName2; - if (chId < 4) { - - chName1 = Form("GM%d", chId); - chName2 = Form("GM%d", chId); - - } else { - - chName1 = Form("GM%d", 4 + (chId - 4) * 2); - chName2 = Form("GM%d", 4 + (chId - 4) * 2 + 1); - } - - for (int i = 0; i < misAlignArray->GetEntries(); ++i) { - - alignMat = (o2::detectors::AlignParam*)misAlignArray->At(i); - TString volName(alignMat->getSymName()); - if ((volName.Contains(chName1) && - ((volName.Last('/') == volName.Index(chName1) + chName1.Length()) || - (volName.Length() == volName.Index(chName1) + chName1.Length()))) || - (volName.Contains(chName2) && - ((volName.Last('/') == volName.Index(chName2) + chName2.Length()) || - (volName.Length() == volName.Index(chName2) + chName2.Length())))) { - - volName.Remove(0, volName.Last('/') + 1); - // if (volName.Contains("GM")){ - // alignMat->SetCorrMatrix(mChCorrMatrix); - // }else if (volName.Contains("DE")){ - // alignMat->SetCorrMatrix(mDECorrMatrix); - // } - } - } - } -} - -//_____________________________________________________ -LocalTrackParam Alignment::RefitStraightTrack(Track& track, Double_t z0) const -{ - - // initialize matrices - TMatrixD AtGASum(4, 4); - AtGASum.Zero(); - - TMatrixD AtGMSum(4, 1); - AtGMSum.Zero(); - - // loop over clusters - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get track parameters - if (!&*itTrackParam) - continue; - - // get cluster - const Cluster* cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // projection matrix - TMatrixD A(2, 4); - A.Zero(); - A(0, 0) = 1; - A(0, 2) = (cluster->getZ() - z0); - A(1, 1) = 1; - A(1, 3) = (cluster->getZ() - z0); - - TMatrixD At(TMatrixD::kTransposed, A); - - // gain matrix - TMatrixD G(2, 2); - G.Zero(); - G(0, 0) = 1.0 / Square(cluster->getEx()); - G(1, 1) = 1.0 / Square(cluster->getEy()); - - const TMatrixD AtG(At, TMatrixD::kMult, G); - const TMatrixD AtGA(AtG, TMatrixD::kMult, A); - AtGASum += AtGA; - - // measurement - TMatrixD M(2, 1); - M(0, 0) = cluster->getX(); - M(1, 0) = cluster->getY(); - const TMatrixD AtGM(AtG, TMatrixD::kMult, M); - AtGMSum += AtGM; - } - - // perform inversion - TMatrixD AtGASumInv(TMatrixD::kInverted, AtGASum); - TMatrixD X(AtGASumInv, TMatrixD::kMult, AtGMSum); - - // // TODO: compare with initial track parameters - // Aliinfo( Form( "x: %.3f vs %.3f", fTrackPos0[0], X(0,0) ) ); - // Aliinfo( Form( "y: %.3f vs %.3f", fTrackPos0[1], X(1,0) ) ); - // Aliinfo( Form( "dxdz: %.6g vs %.6g", fTrackSlope0[0], X(2,0) ) ); - // Aliinfo( Form( "dydz: %.6g vs %.6g\n", fTrackSlope0[1], X(3,0) ) ); - - // fill output parameters - LocalTrackParam out; - out.fTrackX = X(0, 0); - out.fTrackY = X(1, 0); - out.fTrackZ = z0; - out.fTrackSlopeX = X(2, 0); - out.fTrackSlopeY = X(3, 0); - - return out; -} - -//_____________________________________________________ -void Alignment::FillDetElemData(const Cluster* cluster) -{ - // LOG(fatal) << __PRETTY_FUNCTION__ << " is disabled"; - LOG(info) << __PRETTY_FUNCTION__ << " is enabled"; - - /// Get information of current detection element - // get detector element number from Alice ID - const Int_t detElemId = cluster->getDEId(); - fDetElemNumber = GetDetElemNumber(detElemId); - - // get detector element - // const AliMUONGeometryDetElement detElement(detElemId); - auto fTransform = fTransformCreator(detElemId); - /* - get the global transformation matrix and store its inverse, in order to manually perform - the global to Local transformations needed to calculate the derivatives - */ - // fTransform = fTransform.Inverse(); - // fTransform.GetTransformMatrix(fGeoCombiTransInverse); -} - -//______________________________________________________________________ -void Alignment::FillRecPointData(const Cluster* cluster) -{ - - /// Get information of current cluster - fClustPos[0] = cluster->getX(); - fClustPos[1] = cluster->getY(); - fClustPos[2] = cluster->getZ(); -} - -//______________________________________________________________________ -void Alignment::FillTrackParamData(const TrackParam* trackParam) -{ - - /// Get information of current track at current cluster - fTrackPos[0] = trackParam->getNonBendingCoor(); - fTrackPos[1] = trackParam->getBendingCoor(); - fTrackPos[2] = trackParam->getZ(); - fTrackSlope[0] = trackParam->getNonBendingSlope(); - fTrackSlope[1] = trackParam->getBendingSlope(); -} - -//______________________________________________________________________ -void Alignment::LocalEquationX(void) -{ - /// local equation along X - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // local derivatives - SetLocalDerivative(0, r[0]); - SetLocalDerivative(1, r[0] * (fTrackPos[2] - fTrackPos0[2])); - SetLocalDerivative(2, r[1]); - SetLocalDerivative(3, r[1] * (fTrackPos[2] - fTrackPos0[2])); - - // global derivatives - /* - alignment parameters are - 0: delta_x - 1: delta_y - 2: delta_phiz - 3: delta_z - */ - - SetGlobalDerivative(fDetElemNumber * fgNParCh + 0, -r[0]); - SetGlobalDerivative(fDetElemNumber * fgNParCh + 1, -r[1]); - - if (fBFieldOn) { - - // use local position for derivatives vs 'delta_phi_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[1] * fTrackPos[0] + r[0] * fTrackPos[1]); - - // use local slopes for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[0] * fTrackSlope[0] + r[1] * fTrackSlope[1]); - - } else { - - // local copy of extrapolated track positions - const Double_t trackPosX = fTrackPos0[0] + fTrackSlope0[0] * (fTrackPos[2] - fTrackPos0[2]); - const Double_t trackPosY = fTrackPos0[1] + fTrackSlope0[1] * (fTrackPos[2] - fTrackPos0[2]); - - // use properly extrapolated position for derivatives vs 'delta_phi_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[1] * trackPosX + r[0] * trackPosY); - - // use slopes at origin for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[0] * fTrackSlope0[0] + r[1] * fTrackSlope0[1]); - } - - // store local equation - fMillepede->SetLocalEquation(fGlobalDerivatives, fLocalDerivatives, fMeas[0], fSigma[0]); -} - -//______________________________________________________________________ -void Alignment::LocalEquationY(void) -{ - /// local equation along Y - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // store local derivatives - SetLocalDerivative(0, r[3]); - SetLocalDerivative(1, r[3] * (fTrackPos[2] - fTrackPos0[2])); - SetLocalDerivative(2, r[4]); - SetLocalDerivative(3, r[4] * (fTrackPos[2] - fTrackPos0[2])); - - // set global derivatives - SetGlobalDerivative(fDetElemNumber * fgNParCh + 0, -r[3]); - SetGlobalDerivative(fDetElemNumber * fgNParCh + 1, -r[4]); - - if (fBFieldOn) { - - // use local position for derivatives vs 'delta_phi' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[4] * fTrackPos[0] + r[3] * fTrackPos[1]); - - // use local slopes for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[3] * fTrackSlope[0] + r[4] * fTrackSlope[1]); - - } else { - - // local copy of extrapolated track positions - const Double_t trackPosX = fTrackPos0[0] + fTrackSlope0[0] * (fTrackPos[2] - fTrackPos0[2]); - const Double_t trackPosY = fTrackPos0[1] + fTrackSlope0[1] * (fTrackPos[2] - fTrackPos0[2]); - - // use properly extrapolated position for derivatives vs 'delta_phi' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[4] * trackPosX + r[3] * trackPosY); - - // use slopes at origin for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[3] * fTrackSlope0[0] + r[4] * fTrackSlope0[1]); - } - - // store local equation - fMillepede->SetLocalEquation(fGlobalDerivatives, fLocalDerivatives, fMeas[1], fSigma[1]); -} - -//_________________________________________________________________________ -TGeoCombiTrans Alignment::DeltaTransform(const double* lMisAlignment) const -{ - /// Get Delta Transformation, based on alignment parameters - - // translation - const TGeoTranslation deltaTrans(lMisAlignment[0], lMisAlignment[1], lMisAlignment[3]); - - // rotation - TGeoRotation deltaRot; - deltaRot.RotateZ(lMisAlignment[2] * 180. / TMath::Pi()); - - // combined rotation and translation. - return TGeoCombiTrans(deltaTrans, deltaRot); -} - -//______________________________________________________________________ -void Alignment::AddConstraint(Double_t* par, Double_t value) -{ - /// Constrain equation defined by par to value - if (!fInitialized) { - LOG(fatal) << "Millepede is not initialized"; - } - - fMillepede->SetGlobalConstraint(par, value); -} - -//______________________________________________________________________ -Bool_t Alignment::DetElemIsValid(Int_t iDetElemId) const -{ - /// return true if given detector element is valid (and belongs to muon tracker) - const Int_t iCh = iDetElemId / 100; - const Int_t iDet = iDetElemId % 100; - return (iCh > 0 && iCh <= fgNCh && iDet < fgNDetElemCh[iCh - 1]); -} - -//______________________________________________________________________ -Int_t Alignment::GetDetElemNumber(Int_t iDetElemId) const -{ - /// get det element number from ID - // get chamber and element number in chamber - const Int_t iCh = iDetElemId / 100; - const Int_t iDet = iDetElemId % 100; - - // make sure detector index is valid - if (!(iCh > 0 && iCh <= fgNCh && iDet < fgNDetElemCh[iCh - 1])) { - LOG(fatal) << "Invalid detector element id: " << iDetElemId; - } - - // add number of detectors up to this chamber - return iDet + fgSNDetElemCh[iCh - 1]; -} - -//______________________________________________________________________ -Int_t Alignment::GetChamberId(Int_t iDetElemNumber) const -{ - /// get chamber (counting from 1) matching a given detector element id - Int_t iCh(0); - for (iCh = 0; iCh < fgNCh; iCh++) { - if (iDetElemNumber < fgSNDetElemCh[iCh]) - break; - } - - return iCh; -} - -//______________________________________________________________________ -TString Alignment::GetParameterMaskString(UInt_t mask) const -{ - TString out; - if (mask & ParX) - out += "X"; - if (mask & ParY) - out += "Y"; - if (mask & ParZ) - out += "Z"; - if (mask & ParTZ) - out += "T"; - return out; -} - -//______________________________________________________________________ -TString Alignment::GetSidesMaskString(UInt_t mask) const -{ - TString out; - if (mask & SideTop) - out += "T"; - if (mask & SideLeft) - out += "L"; - if (mask & SideBottom) - out += "B"; - if (mask & SideRight) - out += "R"; - return out; -} - -} // namespace mch -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx b/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx index a34d8cc5f2975..86107106dc2ba 100644 --- a/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx +++ b/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx @@ -48,7 +48,7 @@ void NoiseCalibratorSpec::init(InitContext& ic) LOGP(info, "Setting the probability threshold to {} with relative error {}", probT, probTRelErr); mStopMeOnly = ic.options().get("stop-me-only"); mPath = ic.options().get("path-CCDB"); - mPathMerge = ic.options().get("path-CCDB-merge"); + mPathSingle = ic.options().get("path-CCDB-single"); mMeta = ic.options().get("meta"); mStart = ic.options().get("tstart"); @@ -78,12 +78,12 @@ void NoiseCalibratorSpec::run(ProcessingContext& pc) LOG(info) << "Sending an object to Production-CCDBMerge"; sendOutputCcdbMerge(pc.outputs()); } else if (mOutputType.compare("DCS") == 0) { - LOG(info) << "Sending an object to DCS-CCDB"; - sendOutputDcs(pc.outputs()); + LOG(info) << "Sending an object to DCS-Merge"; + sendOutputDcsMerge(pc.outputs()); } else { - LOG(info) << "Sending an object to Production-CCDB and DCS-CCDB"; - sendOutputCcdbDcs(pc.outputs()); - LOG(info) << "Sending an object to Production-CCDBMerge"; + LOG(info) << "Sending an object to Production-CCDB, Production-CCDB-Merge and DCS-Merge"; + sendOutputCcdb(pc.outputs()); + sendOutputDcsMerge(pc.outputs()); sendOutputCcdbMerge(pc.outputs()); } pc.services().get().readyToQuit(mStopMeOnly ? QuitRequest::Me : QuitRequest::All); @@ -102,12 +102,12 @@ void NoiseCalibratorSpec::run(ProcessingContext& pc) LOG(info) << "Sending an object to Production-CCDBMerge"; sendOutputCcdbMerge(pc.outputs()); } else if (mOutputType.compare("DCS") == 0) { - LOG(info) << "Sending an object to DCS-CCDB"; - sendOutputDcs(pc.outputs()); + LOG(info) << "Sending an object to DCS-Merge"; + sendOutputDcsMerge(pc.outputs()); } else { - LOG(info) << "Sending an object to Production-CCDB and DCS-CCDB"; - sendOutputCcdbDcs(pc.outputs()); - LOG(info) << "Sending an object to Production-CCDBMerge"; + LOG(info) << "Sending an object to Production-CCDB, Production-CCDB-Merge and DCS-Merge"; + sendOutputCcdb(pc.outputs()); + sendOutputDcsMerge(pc.outputs()); sendOutputCcdbMerge(pc.outputs()); } pc.services().get().readyToQuit(mStopMeOnly ? QuitRequest::Me : QuitRequest::All); @@ -173,7 +173,7 @@ void NoiseCalibratorSpec::sendOutputCcdbDcs(DataAllocator& output) const auto& payload = mCalibrator->getNoiseMap(); // const auto& payload = mCalibrator->getNoiseMap(starTF, endTF); //For TimeSlot calibration - o2::ccdb::CcdbObjectInfo info(mPath, "NoiseMap", "noise.root", meta, tstart, tend); + o2::ccdb::CcdbObjectInfo info(mPathSingle, "NoiseMap", "noise.root", meta, tstart, tend); auto flName = o2::ccdb::CcdbApi::generateFileName("noise"); auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); info.setFileName(flName); @@ -244,7 +244,7 @@ void NoiseCalibratorSpec::sendOutputCcdb(DataAllocator& output) const auto& payload = mCalibrator->getNoiseMap(); // const auto& payload = mCalibrator->getNoiseMap(starTF, endTF); //For TimeSlot calibration - o2::ccdb::CcdbObjectInfo info(mPath, "NoiseMap", "noise.root", meta, tstart, tend); + o2::ccdb::CcdbObjectInfo info(mPathSingle, "NoiseMap", "noise.root", meta, tstart, tend); auto flName = o2::ccdb::CcdbApi::generateFileName("noise"); auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); info.setFileName(flName); @@ -299,18 +299,18 @@ void NoiseCalibratorSpec::sendOutputCcdbMerge(DataAllocator& output) auto payload = mCalibrator->getNoiseMap(); // const auto& payload = mCalibrator->getNoiseMap(starTF, endTF); //For TimeSlot calibration - map headers; - map filter; - auto* payloadPrev1 = api.retrieveFromTFileAny(mPath, filter, -1, &headers); + std::map headers; + std::map filter; + auto* payloadPrev1 = api.retrieveFromTFileAny(mPathSingle, filter, -1, &headers); long validtime = std::stol(headers["Valid-From"]); auto mergedPL = payload; if (validtime > 0) { validtime = validtime - 1; - auto* payloadPrev2 = api.retrieveFromTFileAny(mPath, filter, validtime, &headers); + auto* payloadPrev2 = api.retrieveFromTFileAny(mPathSingle, filter, validtime, &headers); auto bufferPL = payloadPrev2->merge(payloadPrev1); mergedPL = payload.merge(&bufferPL); } - o2::ccdb::CcdbObjectInfo info(mPathMerge, "NoiseMap", "noise.root", meta, tstart, tend); + o2::ccdb::CcdbObjectInfo info(mPath, "NoiseMap", "noise.root", meta, tstart, tend); auto flName = o2::ccdb::CcdbApi::generateFileName("noise"); auto image = o2::ccdb::CcdbApi::createObjectImage(&mergedPL, &info); info.setFileName(flName); @@ -378,8 +378,77 @@ void NoiseCalibratorSpec::sendOutputDcs(DataAllocator& output) << " : " << infoDcs.getEndValidityTimestamp(); using clbUtilsDcs = o2::calibration::Utils; - output.snapshot(Output{clbUtilsDcs::gDataOriginCDBPayload, "MFT_NoiseMap", 0}, *imageDcs.get()); - output.snapshot(Output{clbUtilsDcs::gDataOriginCDBWrapper, "MFT_NoiseMap", 0}, infoDcs); + output.snapshot(Output{clbUtilsDcs::gDataOriginCDBPayload, "MFT_NoiseMap", 1}, *imageDcs.get()); + output.snapshot(Output{clbUtilsDcs::gDataOriginCDBWrapper, "MFT_NoiseMap", 1}, infoDcs); +} + +void NoiseCalibratorSpec::sendOutputDcsMerge(DataAllocator& output) +{ + + LOG(info) << "DCS-Merge mode"; + + static bool done = false; + if (done) { + return; + } + done = true; + + mCalibrator->finalize(); + + long tstart = mStart; + if (tstart == -1) { + tstart = o2::ccdb::getCurrentTimestamp(); + } + long tend = mEnd; + if (tend == -1) { + constexpr long SECONDSPERYEAR = 365 * 24 * 60 * 60; + tend = o2::ccdb::getFutureTimestamp(SECONDSPERYEAR); + } + + std::map meta; + auto toKeyValPairs = [&meta](std::vector const& tokens) { + for (auto& token : tokens) { + auto keyval = Str::tokenize(token, '=', false); + if (keyval.size() != 2) { + LOG(error) << "Illegal command-line key/value string: " << token; + continue; + } + Str::trim(keyval[1]); + meta[keyval[0]] = keyval[1]; + } + }; + toKeyValPairs(Str::tokenize(mMeta, ';', true)); + + long startTF, endTF; + + auto payload = mCalibrator->getNoiseMap(); + // const auto& payload = mCalibrator->getNoiseMap(starTF, endTF); //For TimeSlot calibration + + std::map headers; + std::map filter; + auto* payloadPrev1 = api.retrieveFromTFileAny(mPathSingle, filter, -1, &headers); + long validtime = std::stol(headers["Valid-From"]); + auto mergedPL = payload; + if (validtime > 0) { + validtime = validtime - 1; + auto* payloadPrev2 = api.retrieveFromTFileAny(mPathSingle, filter, validtime, &headers); + auto bufferPL = payloadPrev2->merge(payloadPrev1); + mergedPL = payload.merge(&bufferPL); + } + + setOutputDcs(mergedPL); + o2::ccdb::CcdbObjectInfo infoDcs(mPathDcs, "NoiseMap", "noise.root", meta, tstart, tend); + auto flNameDcs = o2::ccdb::CcdbApi::generateFileName("noise"); + auto imageDcs = o2::ccdb::CcdbApi::createObjectImage(&mNoiseMapForDcs, &infoDcs); + infoDcs.setFileName(flNameDcs); + LOG(info) << "Sending object " << infoDcs.getPath() << "/" << infoDcs.getFileName() + << " of size " << imageDcs->size() + << " bytes, valid for " << infoDcs.getStartValidityTimestamp() + << " : " << infoDcs.getEndValidityTimestamp(); + + using clbUtilsDcs = o2::calibration::Utils; + output.snapshot(Output{clbUtilsDcs::gDataOriginCDBPayload, "MFT_NoiseMap", 1}, *imageDcs.get()); + output.snapshot(Output{clbUtilsDcs::gDataOriginCDBWrapper, "MFT_NoiseMap", 1}, infoDcs); } void NoiseCalibratorSpec::endOfStream(o2::framework::EndOfStreamContext& ec) @@ -390,11 +459,12 @@ void NoiseCalibratorSpec::endOfStream(o2::framework::EndOfStreamContext& ec) LOG(info) << "Sending an object to Production-CCDB-Merge"; sendOutputCcdbMerge(ec.outputs()); } else if (mOutputType.compare("DCS") == 0) { - LOG(info) << "Sending an object to DCS-CCDB"; - sendOutputDcs(ec.outputs()); + LOG(info) << "Sending an object to DCS-Merge"; + sendOutputDcsMerge(ec.outputs()); } else { - LOG(info) << "Sending an object to Production-CCDB and DCS-CCDB"; - sendOutputCcdbDcs(ec.outputs()); + LOG(info) << "Sending an object to Production-CCDB, Production-CCDB-Merge and DCS-Merge"; + sendOutputCcdb(ec.outputs()); + sendOutputDcsMerge(ec.outputs()); sendOutputCcdbMerge(ec.outputs()); } } @@ -454,7 +524,7 @@ DataProcessorSpec getNoiseCalibratorSpec(bool useDigits) {"tstart", VariantType::Int64, -1ll, {"Start of validity timestamp"}}, {"tend", VariantType::Int64, -1ll, {"End of validity timestamp"}}, {"path-CCDB", VariantType::String, "/MFT/Calib/NoiseMap", {"Path to write to in CCDB"}}, - {"path-CCDB-merge", VariantType::String, "/MFT/Calib/NoiseMapMerged", {"Path to write merged file to in CCDB"}}, + {"path-CCDB-single", VariantType::String, "/MFT/Calib/NoiseMapSingle", {"Path to write merged file to in CCDB"}}, {"path-DCS", VariantType::String, "/MFT/Config/NoiseMap", {"Path to write to in DCS"}}, {"meta", VariantType::String, "", {"meta data to write in CCDB"}}, {"send-to-server", VariantType::String, "CCDB-DCS", {"meta data to write in DCS-CCDB"}}, diff --git a/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx b/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx deleted file mode 100644 index 13d6f3b3f567b..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file NoiseSlotCalibrator.cxx - -#include "MFTCalibration/NoiseSlotCalibrator.h" - -#include -#include "TFile.h" -#include "DataFormatsITSMFT/Digit.h" -#include "DataFormatsITSMFT/ClusterPattern.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -namespace o2 -{ -using Slot = calibration::TimeSlot; - -namespace mft -{ -bool NoiseSlotCalibrator::processTimeFrame(calibration::TFType nTF, - gsl::span const& digits, - gsl::span const& rofs) -{ - LOG(detail) << "Processing TF# " << nTF; - - auto& slotTF = getSlotForTF(nTF); - auto& noiseMap = *(slotTF.getContainer()); - - for (const auto& rof : rofs) { - auto digitsInFrame = rof.getROFData(digits); - for (const auto& d : digitsInFrame) { - auto id = d.getChipIndex(); - auto row = d.getRow(); - auto col = d.getColumn(); - - noiseMap.increaseNoiseCount(id, row, col); - } - } - noiseMap.addStrobes(rofs.size()); - mNumberOfStrobes += rofs.size(); - return hasEnoughData(slotTF); -} - -bool NoiseSlotCalibrator::processTimeFrame(calibration::TFType nTF, - gsl::span const& clusters, - gsl::span const& patterns, - gsl::span const& rofs) -{ - LOG(detail) << "Processing TF# " << nTF; - - auto& slotTF = getSlotForTF(nTF); - auto& noiseMap = *(slotTF.getContainer()); - - auto pattIt = patterns.begin(); - for (const auto& rof : rofs) { - auto clustersInFrame = rof.getROFData(clusters); - for (const auto& c : clustersInFrame) { - if (c.getPatternID() != o2::itsmft::CompCluster::InvalidPatternID) { - // For the noise calibration, we use "pass1" clusters... - continue; - } - o2::itsmft::ClusterPattern patt(pattIt); - - auto id = c.getSensorID(); - auto row = c.getRow(); - auto col = c.getCol(); - auto colSpan = patt.getColumnSpan(); - auto rowSpan = patt.getRowSpan(); - - // Fast 1-pixel calibration - if ((rowSpan == 1) && (colSpan == 1)) { - noiseMap.increaseNoiseCount(id, row, col); - continue; - } - - // All-pixel calibration - auto nBits = rowSpan * colSpan; - int ic = 0, ir = 0; - for (unsigned int i = 2; i < patt.getUsedBytes() + 2; i++) { - unsigned char tempChar = patt.getByte(i); - int s = 128; // 0b10000000 - while (s > 0) { - if ((tempChar & s) != 0) { - noiseMap.increaseNoiseCount(id, row + ir, col + ic); - } - ic++; - s >>= 1; - if ((ir + 1) * ic == nBits) { - break; - } - if (ic == colSpan) { - ic = 0; - ir++; - } - } - if ((ir + 1) * ic == nBits) { - break; - } - } - } - } - noiseMap.addStrobes(rofs.size()); - mNumberOfStrobes += rofs.size(); - return hasEnoughData(slotTF); -} - -// Functions overloaded from the calibration framework -bool NoiseSlotCalibrator::process(calibration::TFType tf, const gsl::span data) -{ - LOG(warning) << "Only 1-pix noise calibraton is possible !"; - return calibration::TimeSlotCalibration::process(tf, data); -} - -// Functions required by the calibration framework - -Slot& NoiseSlotCalibrator::emplaceNewSlot(bool front, calibration::TFType tstart, calibration::TFType tend) -{ - auto& cont = getSlots(); - auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique(936)); - return slot; -} - -bool NoiseSlotCalibrator::hasEnoughData(const Slot& slot) const -{ - return slot.getContainer()->getNumberOfStrobes() > mMinROFs ? true : false; -} - -void NoiseSlotCalibrator::finalizeSlot(Slot& slot) -{ - o2::itsmft::NoiseMap* map = slot.getContainer(); - LOG(info) << "Number of processed strobes is " << map->getNumberOfStrobes(); - map->applyProbThreshold(mProbabilityThreshold, map->getNumberOfStrobes(), mProbRelErr); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/condition/macros/readAlpideCCDB.C b/Detectors/ITSMFT/MFT/condition/macros/readAlpideCCDB.C index 31b8028e2b7ee..adc8aaa16ed70 100644 --- a/Detectors/ITSMFT/MFT/condition/macros/readAlpideCCDB.C +++ b/Detectors/ITSMFT/MFT/condition/macros/readAlpideCCDB.C @@ -14,7 +14,7 @@ void readAlpideCCDB(long timestamp = -1, float thresh = 0) o2::ccdb::CcdbApi api; // api.init("alice-ccdb.cern.ch"); api.init("ccdb-test.cern.ch"); - map headers; + std::map headers; map filter; auto calib = api.retrieveFromTFileAny>("MFT/Config/AlpideParam/", filter, timestamp, &headers); calib->printKeyValues(); diff --git a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt index acb3d0b3e835f..b83699498a6b8 100644 --- a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt @@ -12,8 +12,6 @@ o2_add_library(MFTWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackReaderSpec.cxx src/TrackWriterSpec.cxx diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index c8061310e34f6..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.cxx - -#include - -#include "MFTWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace mft -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "MFTClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("mft-cluster-writer", - "mftclusters.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with MFT clusters"}, - BranchDefinition{InputSpec{"compclus", "MFT", "COMPCLUSTERS", 0}, - "MFTClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "MFT", "PATTERNS", 0}, - "MFTClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "MFT", "CLUSTERSROF", 0}, - "MFTClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "MFT", "CLUSTERSMCTR", 0}, - "MFTClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0}, - "MFTClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx deleted file mode 100644 index 766d7c1a0729e..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingMFT.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "DetectorsBase/GeometryManager.h" -#include "MFTBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTReconstruction/ClustererParam.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace mft -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingMFT::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - const o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(debug) << "MFTClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginMFT; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(debug) << "MFTClusterer pushed " << clusCompVec.size() << " compressed clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(nROFsToSquash); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const TopologyDictionary*)obj); - } - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "MFT", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "MFT", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "MFT", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "MFT", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/ClustererParam")); - inputs.emplace_back("alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "MFT", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "mft-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 615c9c1b275d4..5d85c0ef81670 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -12,9 +12,9 @@ /// @file RecoWorkflow.cxx #include +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/RecoWorkflow.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "MFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/TrackerSpec.h" #include "MFTWorkflow/TrackWriterSpec.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" @@ -52,10 +52,10 @@ framework::WorkflowSpec getWorkflow( } } if (!upstreamClusters) { - specs.emplace_back(o2::mft::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); } if (runTracking) { diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx index f42b2e0c92a4a..b656970693808 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "MFTWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -34,6 +34,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); WorkflowSpec specs; - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h index bc3b3dbde53b0..de39bed299634 100644 --- a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h +++ b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h @@ -26,17 +26,44 @@ constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~ template struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper> { + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } static constexpr std::string_view getParamName() { return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; } - int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuos mode - float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode - float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start - float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay - float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) - int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS + + int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuous mode + float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode + float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start + float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay + float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) + int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS + int roFrameLayerLengthInBC[getNLayers()] = {}; ///< staggering ROF length in BC for continuous mode per layer + int roFrameLayerBiasInBC[getNLayers()] = {}; ///< staggering ROF bias in BC for continuous mode per layer + int roFrameLayerDelayInBC[getNLayers()] = {}; ///< staggering ROF delay in BC for continuous mode per layer + + static constexpr bool supportsStaggering() noexcept { return (N == o2::detectors::DetID::ITS) ? false : false; } + // test if staggering is on + bool withStaggering() const noexcept + { + if constexpr (!supportsStaggering()) { + return false; + } + for (int i{0}; i < getNLayers(); ++i) { + if (roFrameLayerLengthInBC[i] != 0) { + return true; + } + } + return false; + } + // get ROF length for any layer + int getROFLengthInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerLengthInBC[layer] : roFrameLengthInBC; } + int getROFBiasInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerBiasInBC[layer] : roFrameBiasInBC; } + int getROFDelayInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerDelayInBC[layer] : 0; } // boilerplate stuff + make principal key O2ParamDef(DPLAlpideParam, getParamName().data()); @@ -46,7 +73,7 @@ struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper, ClusterPattern::MaxColSpan + 2>; using RowColBuff = std::vector; - CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), det) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, det, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index 960ce2ca33d5b..0bdbb701a9356 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -121,6 +121,10 @@ class Clusterer }; struct ClustererThread { + struct PreCluster { + int head = 0; // index of precluster head in the pixels + int index = 0; + }; int id = -1; Clusterer* parent = nullptr; // parent clusterer // buffers for entries in preClusterIndices in 2 columns, to avoid boundary checks, we reserve @@ -132,12 +136,11 @@ class Clusterer // pixels[].first is the index of the next pixel of the same precluster in the pixels // pixels[].second is the index of the referred pixel in the ChipPixelData (element of mChips) std::vector> pixels; - std::vector preClusterHeads; // index of precluster head in the pixels - std::vector preClusterIndices; uint16_t currCol = 0xffff; ///< Column being processed bool noLeftCol = true; ///< flag that there is no column on the left to check std::array labelsBuff; //! temporary buffer for building cluster labels std::vector pixArrBuff; //! temporary buffer for pattern calc. + std::vector preClusters; //! preclusters info // /// temporary storage for the thread output CompClusCont compClusters; @@ -154,7 +157,7 @@ class Clusterer ///< add cluster at row (entry ip in the ChipPixeData) to the precluster with given index void expandPreCluster(uint32_t ip, uint16_t row, int preClusIndex) { - auto& firstIndex = preClusterHeads[preClusterIndices[preClusIndex]]; + auto& firstIndex = preClusters[preClusters[preClusIndex].index].head; pixels.emplace_back(firstIndex, ip); firstIndex = pixels.size() - 1; curr[row] = preClusIndex; @@ -163,11 +166,10 @@ class Clusterer ///< add new precluster at given row of current column for the fired pixel with index ip in the ChipPixelData void addNewPrecluster(uint32_t ip, uint16_t row) { - preClusterHeads.push_back(pixels.size()); + int lastIndex = preClusters.size(); + preClusters.emplace_back(pixels.size(), lastIndex); // new head does not point yet (-1) on other pixels, store just the entry of the pixel in the ChipPixelData pixels.emplace_back(-1, ip); - int lastIndex = preClusterIndices.size(); - preClusterIndices.push_back(lastIndex); curr[row] = lastIndex; // store index of the new precluster in the current column buffer } @@ -213,13 +215,15 @@ class Clusterer int getMaxRowColDiffToMask() const { return mMaxRowColDiffToMask; } void setMaxRowColDiffToMask(int v) { mMaxRowColDiffToMask = v; } - int getMaxROFDepthToSquash() const { return mSquashingDepth; } + int getMaxROFDepthToSquash(int layer = -1) const { return (layer < 0) ? mSquashingDepth : mSquashingLayerDepth[layer]; } void setMaxROFDepthToSquash(int v) { mSquashingDepth = v; } + void addMaxROFDepthToSquash(int v) { mSquashingLayerDepth.push_back(v); } - int getMaxBCSeparationToSquash() const { return mMaxBCSeparationToSquash; } + int getMaxBCSeparationToSquash(int layer = -1) const { return (layer < 0) ? mMaxBCSeparationToSquash : mMaxBCSeparationToSquashLayer[layer]; } void setMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquash = n; } + void addMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquashLayer.push_back(n); } - void print() const; + void print(bool showTiming = true) const; void clear(); void reset(); @@ -243,7 +247,7 @@ class Clusterer bool mContinuousReadout = true; ///< flag continuous readout bool mDropHugeClusters = false; ///< don't include clusters that would be split in more than one - ///< mask continuosly fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) + ///< mask continuously fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) int mMaxBCSeparationToMask = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; int mMaxRowColDiffToMask = 0; ///< provide their difference in col/row is <= than this int mNHugeClus = 0; ///< number of encountered huge clusters @@ -251,6 +255,8 @@ class Clusterer ///< Squashing options int mSquashingDepth = 0; ///< squashing is applied to next N rofs int mMaxBCSeparationToSquash = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; + std::vector mSquashingLayerDepth; + std::vector mMaxBCSeparationToSquashLayer; std::vector> mThreads; // buffers for threads std::vector mChips; // currently processed ROF's chips data @@ -286,7 +292,7 @@ void Clusterer::streamCluster(const std::vector& pixbuf, const std::a uint16_t row = bbox.rowMin, col = bbox.colMin; if (pattID == CompCluster::InvalidPatternID || pattIdConverter.isGroup(pattID)) { if (pattID != CompCluster::InvalidPatternID) { - // For groupped topologies, the reference pixel is the COG pixel + // For grouped topologies, the reference pixel is the COG pixel float xCOG = 0., zCOG = 0.; ClusterPattern::getCOG(rowSpanW, colSpanW, patt.data(), xCOG, zCOG); row += round(xCOG); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h index a71e5f3095b06..3188a4f3b0010 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h @@ -29,16 +29,26 @@ template struct ClustererParam : public o2::conf::ConfigurableParamHelper> { static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS or DetID:: MFT are allowed"); + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } + static constexpr std::string_view getParamName() { return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; } - int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired - int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking - int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing - float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus - bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired + int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking + int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing + float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus + bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxBCDiffToSquashBiasLayer[getNLayers()] = {}; ///< squash mask per layer + int getMaxBCDiffToSquashBias(int layer) const noexcept + { + return maxBCDiffToSquashBiasLayer[layer] ? maxBCDiffToSquashBiasLayer[layer] : maxBCDiffToSquashBias; + } O2ParamDef(ClustererParam, getParamName().data()); @@ -46,7 +56,7 @@ struct ClustererParam : public o2::conf::ConfigurableParamHelper(nFired, nThreads); #ifndef WITH_OPENMP nThreads = 1; #endif @@ -133,15 +128,17 @@ void Clusterer::process(int nThreads, PixelReader& reader, CompClusCont* compClu if (stat.firstChip == chid) { thrStatIdx[ith]++; chid += stat.nChips; // next chip to look - const auto clbeg = mThreads[ith]->compClusters.begin() + stat.firstClus; - auto szold = compClus->size(); - compClus->insert(compClus->end(), clbeg, clbeg + stat.nClus); - if (patterns) { - const auto ptbeg = mThreads[ith]->patterns.begin() + stat.firstPatt; - patterns->insert(patterns->end(), ptbeg, ptbeg + stat.nPatt); - } - if (labelsCl) { - labelsCl->mergeAtBack(mThreads[ith]->labels, stat.firstClus, stat.nClus); + if (stat.nClus > 0) { + const auto clbeg = mThreads[ith]->compClusters.begin() + stat.firstClus; + auto szold = compClus->size(); + compClus->insert(compClus->end(), clbeg, clbeg + stat.nClus); + if (patterns) { + const auto ptbeg = mThreads[ith]->patterns.begin() + stat.firstPatt; + patterns->insert(patterns->end(), ptbeg, ptbeg + stat.nPatt); + } + if (labelsCl) { + labelsCl->mergeAtBack(mThreads[ith]->labels, stat.firstClus, stat.nClus); + } } } } @@ -171,7 +168,7 @@ void Clusterer::ClustererThread::process(uint16_t chip, uint16_t nChips, CompClu const ConstMCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr) { if (stats.empty() || stats.back().firstChip + stats.back().nChips != chip) { // there is a jump, register new block - stats.emplace_back(ThreadStat{chip, 0, uint32_t(compClusPtr->size()), patternsPtr ? uint32_t(patternsPtr->size()) : 0, 0, 0}); + stats.emplace_back(ThreadStat{.firstChip = chip, .nChips = 0, .firstClus = uint32_t(compClusPtr->size()), .firstPatt = patternsPtr ? uint32_t(patternsPtr->size()) : 0, .nClus = 0, .nPatt = 0}); } for (int ic = 0; ic < nChips; ic++) { auto* curChipData = parent->mFiredChipsPtr[chip + ic]; @@ -214,14 +211,22 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus PatternCont* patternsPtr, const ConstMCTruth* labelsDigPtr, MCTruth* labelsClusPtr) { const auto& pixData = curChipData->getData(); - for (int i1 = 0; i1 < preClusterHeads.size(); ++i1) { - auto ci = preClusterIndices[i1]; + int nPreclusters = preClusters.size(); + // account for the eventual reindexing of preClusters: Id2 might have been reindexed to Id1, which later was reindexed to Id0 + for (int i = 1; i < nPreclusters; i++) { + if (preClusters[i].index != i) { // reindexing is always done towards smallest index + preClusters[i].index = preClusters[preClusters[i].index].index; + } + } + for (int i1 = 0; i1 < nPreclusters; ++i1) { + auto& preCluster = preClusters[i1]; + auto ci = preCluster.index; if (ci < 0) { continue; } BBox bbox(curChipData->getChipID()); int nlab = 0; - int next = preClusterHeads[i1]; + int next = preCluster.head; pixArrBuff.clear(); while (next >= 0) { const auto& pixEntry = pixels[next]; @@ -237,12 +242,13 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus } next = pixEntry.first; } - preClusterIndices[i1] = -1; - for (int i2 = i1 + 1; i2 < preClusterHeads.size(); ++i2) { - if (preClusterIndices[i2] != ci) { + preCluster.index = -1; + for (int i2 = i1 + 1; i2 < nPreclusters; ++i2) { + auto& preCluster2 = preClusters[i2]; + if (preCluster2.index != ci) { continue; } - next = preClusterHeads[i2]; + next = preCluster2.head; while (next >= 0) { const auto& pixEntry = pixels[next]; const auto pix = pixData[pixEntry.second]; // PixelData @@ -257,7 +263,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus } next = pixEntry.first; } - preClusterIndices[i2] = -1; + preCluster2.index = -1; } if (bbox.isAcceptableSize()) { parent->streamCluster(pixArrBuff, &labelsBuff, bbox, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab); @@ -344,18 +350,15 @@ void Clusterer::ClustererThread::initChip(const ChipPixelData* curChipData, uint prev = column1 + 1; curr = column2 + 1; resetColumn(curr); - pixels.clear(); - preClusterHeads.clear(); - preClusterIndices.clear(); + preClusters.clear(); auto pix = curChipData->getData()[first]; currCol = pix.getCol(); curr[pix.getRowDirect()] = 0; // can use getRowDirect since the pixel is not masked // start the first pre-cluster - preClusterHeads.push_back(0); - preClusterIndices.push_back(0); + preClusters.emplace_back(); pixels.emplace_back(-1, first); // id of current pixel - noLeftCol = true; // flag that there is no column on the left to check yet + noLeftCol = true; } //__________________________________________________ @@ -378,39 +381,58 @@ void Clusterer::ClustererThread::updateChip(const ChipPixelData* curChipData, ui currCol = pix.getCol(); } - Bool_t orphan = true; - if (noLeftCol) { // check only the row above if (curr[row - 1] >= 0) { expandPreCluster(ip, row, curr[row - 1]); // attach to the precluster of the previous row - return; + } else { + addNewPrecluster(ip, row); // start new precluster } } else { + // row above should be always checked + int nnb = 0, lowestIndex = curr[row - 1], lowestNb = 0, *nbrCol[4], nbrRow[4]; + if (lowestIndex >= 0) { + nbrCol[nnb] = curr; + nbrRow[nnb++] = row - 1; + } else { + lowestIndex = 0x7ffff; + lowestNb = -1; + } #ifdef _ALLOW_DIAGONAL_ALPIDE_CLUSTERS_ - int neighbours[]{curr[row - 1], prev[row], prev[row + 1], prev[row - 1]}; -#else - int neighbours[]{curr[row - 1], prev[row]}; -#endif - for (auto pci : neighbours) { - if (pci < 0) { - continue; + for (int i : {-1, 0, 1}) { + auto v = prev[row + i]; + if (v >= 0) { + nbrCol[nnb] = prev; + nbrRow[nnb] = row + i; + if (v < lowestIndex) { + lowestIndex = v; + lowestNb = nnb; + } + nnb++; } - if (orphan) { - expandPreCluster(ip, row, pci); // attach to the adjascent precluster - orphan = false; - continue; + } +#else + if (prev[row] >= 0) { + nbrCol[nnb] = prev; + nbrRow[nnb] = row; + if (prev[row] < lowestIndex) { + lowestIndex = v; + lowestNb = nnb; } - // reassign precluster index to smallest one - if (preClusterIndices[pci] < preClusterIndices[curr[row]]) { - preClusterIndices[curr[row]] = preClusterIndices[pci]; - } else { - preClusterIndices[pci] = preClusterIndices[curr[row]]; + nnb++; + } +#endif + if (!nnb) { // no neighbours, create new precluster + addNewPrecluster(ip, row); // start new precluster + } else { + expandPreCluster(ip, row, lowestIndex); // attach to the adjascent precluster with smallest index + if (nnb > 1) { + for (int inb = 0; inb < nnb; inb++) { // reassign precluster index to smallest one, replicating updated values to columns caches + auto& prevIndex = (nbrCol[inb])[nbrRow[inb]]; + prevIndex = preClusters[prevIndex].index = lowestIndex; + } } } } - if (orphan) { - addNewPrecluster(ip, row); // start new precluster - } } //__________________________________________________ @@ -449,22 +471,31 @@ void Clusterer::clear() } //__________________________________________________ -void Clusterer::print() const +void Clusterer::print(bool showsTiming) const { // print settings - LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + if (mSquashingLayerDepth.empty()) { + LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + } else { + LOGP(info, "Clusterizer squashes overflow pixels <= {} in row/col", mMaxRowColDiffToMask); + for (size_t i{0}; i < mSquashingLayerDepth.size(); ++i) { + LOGP(info, "\tlay:{} separated by {} BC seeking down to {} neighbour ROFs", i, mMaxBCSeparationToSquashLayer[i], mSquashingLayerDepth[i]); + } + } LOGP(info, "Clusterizer masks overflow pixels separated by < {} BC and <= {} in row/col", mMaxBCSeparationToMask, mMaxRowColDiffToMask); LOGP(info, "Clusterizer does {} drop huge clusters", mDropHugeClusters ? "" : "not"); + if (showsTiming) { #ifdef _PERFORM_TIMING_ - auto& tmr = const_cast(mTimer); // ugly but this is what root does internally - auto& tmrm = const_cast(mTimerMerge); - LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() - << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; - LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() - << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; + auto& tmr = const_cast(mTimer); // ugly but this is what root does internally + auto& tmrm = const_cast(mTimerMerge); + LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() + << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; + LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() + << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; #endif + } } //__________________________________________________ diff --git a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx index b8d88a6fc4223..5c1dbde074649 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx @@ -330,3 +330,14 @@ void DigitPixelReader::clear() mROFRecVec = gsl::span(); mMC2ROFRecVec = gsl::span(); } + +//______________________________________________________________________________ +void DigitPixelReader::reset() +{ + clear(); + mSquashedDigitsMask.clear(); + mBookmarkNextROFs.clear(); + mIdDig = 0; + mIdROF = 0; + mIdROFLast = 0; +} diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h index b27739c26bc4d..fa75a65728675 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h @@ -15,8 +15,10 @@ #ifndef ALICEO2_ITSMFT_DIGIPARAMS_H #define ALICEO2_ITSMFT_DIGIPARAMS_H +#include +#include #include -#include +#include "ITSMFTSimulation/AlpideSignalTrapezoid.h" #include "ITSMFTBase/DPLAlpideParam.h" //////////////////////////////////////////////////////////// @@ -51,24 +53,24 @@ class DigiParams void setContinuous(bool v) { mIsContinuous = v; } bool isContinuous() const { return mIsContinuous; } - int getROFrameLengthInBC() const { return mROFrameLengthInBC; } - void setROFrameLengthInBC(int n) { mROFrameLengthInBC = n; } + int getROFrameLengthInBC(int layer = -1) const { return layer < 0 ? mROFrameLengthInBC : mROFrameLayerLengthInBC[layer]; } + void setROFrameLengthInBC(int n, int layer = -1) { layer < 0 ? mROFrameLengthInBC = n : mROFrameLayerLengthInBC[layer] = n; } - void setROFrameLength(float ns); - float getROFrameLength() const { return mROFrameLength; } - float getROFrameLengthInv() const { return mROFrameLengthInv; } + void setROFrameLength(float ns, int layer = -1); + float getROFrameLength(int layer = -1) const { return layer < 0 ? mROFrameLength : mROFrameLayerLength[layer]; } + float getROFrameLengthInv(int layer = -1) const { return layer < 0 ? mROFrameLengthInv : mROFrameLayerLengthInv[layer]; } void setStrobeDelay(float ns) { mStrobeDelay = ns; } - float getStrobeDelay() const { return mStrobeDelay; } + float getStrobeDelay(int layer = -1) const { return layer < 0 ? mStrobeDelay : mStrobeLayerDelay[layer]; } void setStrobeLength(float ns) { mStrobeLength = ns; } - float getStrobeLength() const { return mStrobeLength; } + float getStrobeLength(int layer = -1) const { return layer < 0 ? mStrobeLength : mStrobeLayerLength[layer]; } void setTimeOffset(double sec) { mTimeOffset = sec; } double getTimeOffset() const { return mTimeOffset; } - void setROFrameBiasInBC(int n) { mROFrameBiasInBC = n; } - int getROFrameBiasInBC() const { return mROFrameBiasInBC; } + void setROFrameBiasInBC(int n, int layer = -1) { layer < 0 ? mROFrameBiasInBC = n : mROFrameLayerBiasInBC[layer] = n; } + int getROFrameBiasInBC(int layer = -1) const { return layer < 0 ? mROFrameBiasInBC : mROFrameLayerBiasInBC[layer]; } void setChargeThreshold(int v, float frac2Account = 0.1); void setNSimSteps(int v); @@ -96,13 +98,19 @@ class DigiParams const SignalShape& getSignalShape() const { return mSignalShape; } SignalShape& getSignalShape() { return (SignalShape&)mSignalShape; } + bool withStaggering() const noexcept { return !mROFrameLayerLength.empty(); } + void addROFrameLayerLengthInBC(int len) { mROFrameLayerLengthInBC.push_back(len); } + void addROFrameLayerBiasInBC(int len) { mROFrameLayerBiasInBC.push_back(len); } + void addStrobeLength(float ns) { mStrobeLayerLength.push_back(ns); } + void addStrobeDelay(float ns) { mStrobeLayerDelay.push_back(ns); } + virtual void print() const; private: static constexpr double infTime = 1e99; bool mIsContinuous = false; ///< flag for continuous simulation float mNoisePerPixel = 1.e-8; ///< ALPIDE Noise per chip - int mROFrameLengthInBC = 0; ///< ROF length in BC for continuos mode + int mROFrameLengthInBC = 0; ///< ROF length in BC for continuous mode float mROFrameLength = 0; ///< length of RO frame in ns float mStrobeDelay = 0.; ///< strobe start (in ns) wrt ROF start float mStrobeLength = 0; ///< length of the strobe in ns (sig. over threshold checked in this window only) @@ -115,17 +123,24 @@ class DigiParams float mVbb = 0.0; ///< back bias absolute value for MFT (in Volt) float mIBVbb = 0.0; ///< back bias absolute value for ITS Inner Barrel (in Volt) - float mOBVbb = 0.0; ///< back bias absolute value for ITS Outter Barrel (in Volt) + float mOBVbb = 0.0; ///< back bias absolute value for ITS Outer Barrel (in Volt) + + std::vector mROFrameLayerLengthInBC; ///< staggering ROF length in BC for continuous mode per layer + std::vector mROFrameLayerBiasInBC; ///< staggering ROF bias in BC for continuous mode per layer + std::vector mROFrameLayerLength; ///< staggering ROF length in ns for continuous mode per layer + std::vector mStrobeLayerLength; ///< staggering length of the strobe in ns (sig. over threshold checked in this window only) + std::vector mStrobeLayerDelay; ///< staggering delay of the strobe in ns o2::itsmft::AlpideSignalTrapezoid mSignalShape; ///< signal timeshape parameterization const o2::itsmft::AlpideSimResponse* mAlpSimResponse = nullptr; //!< pointer on external response // auxiliary precalculated parameters - float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns - float mNSimStepsInv = 0; ///< its inverse + float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns + std::vector mROFrameLayerLengthInv; // inverse length of RO frame in ns per layer + float mNSimStepsInv = 0; ///< its inverse - ClassDef(DigiParams, 2); + ClassDef(DigiParams, 3); }; } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h index e3995068c52cf..c81e2d9476644 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h @@ -49,6 +49,8 @@ class Digitizer : public TObject public: Digitizer() = default; + Digitizer(Digitizer&&) = delete; + Digitizer& operator=(Digitizer&&) = delete; ~Digitizer() override = default; Digitizer(const Digitizer&) = delete; Digitizer& operator=(const Digitizer&) = delete; @@ -56,27 +58,28 @@ class Digitizer : public TObject void setDigits(std::vector* dig) { mDigits = dig; } void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } void setROFRecords(std::vector* rec) { mROFRecords = rec; } - o2::itsmft::DigiParams& getParams() { return (o2::itsmft::DigiParams&)mParams; } + o2::itsmft::DigiParams& getParams() { return mParams; } const o2::itsmft::DigiParams& getParams() const { return mParams; } void setNoiseMap(const o2::itsmft::NoiseMap* mp) { mNoiseMap = mp; } void setDeadChannelsMap(const o2::itsmft::NoiseMap* mp) { mDeadChanMap = mp; } void init(); + void setAlpideResponse(const o2::itsmft::AlpideSimResponse* resp, int i) { mAlpSimResp[i] = resp; } auto getChipResponse(int chipID); /// Steer conversion of hits to digits - void process(const std::vector* hits, int evID, int srcID); - void setEventTime(const o2::InteractionTimeRecord& irt); + void process(const std::vector* hits, int evID, int srcID, int layer = -1); + void setEventTime(const o2::InteractionTimeRecord& irt, int layer = -1); double getEndTimeOfROFMax() const { ///< return the time corresponding to end of the last reserved ROFrame : mROFrameMax - return mParams.getROFrameLength() * (mROFrameMax + 1) + mParams.getTimeOffset(); + return (mParams.getROFrameLength() * (double)(mROFrameMax + 1)) + mParams.getTimeOffset(); } void setContinuous(bool v) { mParams.setContinuous(v); } bool isContinuous() const { return mParams.isContinuous(); } - void fillOutputContainer(uint32_t maxFrame = 0xffffffff); + void fillOutputContainer(uint32_t maxFrame = 0xffffffff, int layer = -1); void setDigiParams(const o2::itsmft::DigiParams& par) { mParams = par; } const o2::itsmft::DigiParams& getDigitParams() const { return mParams; } @@ -91,11 +94,17 @@ class Digitizer : public TObject mEventROFrameMin = 0xffffffff; mEventROFrameMax = 0; } + void resetROFrameBounds() + { + mROFrameMin = 0; + mROFrameMax = 0; + mNewROFrame = 0; + } private: - void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID); + void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID, int lay); void registerDigits(ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, - uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl); + uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl, int lay); ExtraDig* getExtraDigBuffer(uint32_t roFrame) { @@ -114,7 +123,7 @@ class Digitizer : public TObject o2::itsmft::DigiParams mParams; ///< digitization parameters o2::InteractionTimeRecord mEventTime; ///< global event time and interaction record o2::InteractionRecord mIRFirstSampledTF; ///< IR of the 1st sampled IR, noise-only ROFs will be inserted till this IR only - double mCollisionTimeWrtROF; + double mCollisionTimeWrtROF{}; uint32_t mROFrameMin = 0; ///< lowest RO frame of current digits uint32_t mROFrameMax = 0; ///< highest RO frame of current digits uint32_t mNewROFrame = 0; ///< ROFrame corresponding to provided time @@ -124,11 +133,10 @@ class Digitizer : public TObject uint32_t mEventROFrameMax = 0; ///< highest RO frame forfor processed events (w/o automatic noise ROFs) int mNumberOfChips = 0; - o2::itsmft::AlpideSimResponse* mAlpSimRespMFT = nullptr; - o2::itsmft::AlpideSimResponse* mAlpSimRespIB = nullptr; - o2::itsmft::AlpideSimResponse* mAlpSimRespOB = nullptr; - o2::itsmft::AlpideSimResponse mAlpSimResp[2]; // simulated response - std::string mResponseFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + const o2::itsmft::AlpideSimResponse* mAlpSimRespMFT = nullptr; + const o2::itsmft::AlpideSimResponse* mAlpSimRespIB = nullptr; + const o2::itsmft::AlpideSimResponse* mAlpSimRespOB = nullptr; + const o2::itsmft::AlpideSimResponse* mAlpSimResp[2]; // simulated response const o2::itsmft::GeometryTGeo* mGeometry = nullptr; ///< ITS OR MFT upgrade geometry std::vector mChips; ///< Array of chips digits containers diff --git a/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx b/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx index ffba627265cc7..a7c5c32b6351d 100644 --- a/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx +++ b/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx @@ -26,12 +26,17 @@ DigiParams::DigiParams() setNSimSteps(mNSimSteps); } -void DigiParams::setROFrameLength(float lNS) +void DigiParams::setROFrameLength(float lNS, int layer) { // set ROFrame length in nanosecongs - mROFrameLength = lNS; - assert(mROFrameLength > 1.); - mROFrameLengthInv = 1. / mROFrameLength; + assert(lNS > 1.f); + if (layer < 0) { + mROFrameLength = lNS; + mROFrameLengthInv = 1.f / mROFrameLength; + } else { + mROFrameLayerLength.push_back(lNS); + mROFrameLayerLengthInv.push_back(1.f / lNS); + } } void DigiParams::setNSimSteps(int v) @@ -58,17 +63,24 @@ void DigiParams::setChargeThreshold(int v, float frac2Account) //______________________________________________ void DigiParams::print() const { - // print settings - printf("Alpide digitization params:\n"); - printf("Continuous readout : %s\n", mIsContinuous ? "ON" : "OFF"); - printf("Readout Frame Length(ns) : %f\n", mROFrameLength); - printf("Strobe delay (ns) : %f\n", mStrobeDelay); - printf("Strobe length (ns) : %f\n", mStrobeLength); - printf("Threshold (N electrons) : %d\n", mChargeThreshold); - printf("Min N electrons to account : %d\n", mMinChargeToAccount); - printf("Number of charge sharing steps : %d\n", mNSimSteps); - printf("ELoss to N electrons factor : %e\n", mEnergyToNElectrons); - printf("Noise level per pixel : %e\n", mNoisePerPixel); - printf("Charge time-response:\n"); + LOGF(info, "Alpide digitization params:"); + LOGF(info, "Continuous readout : %s", mIsContinuous ? "ON" : "OFF"); + if (withStaggering()) { + for (int i{0}; i < (int)mROFrameLayerLengthInBC.size(); ++i) { + LOGF(info, " Readout Frame Layer:%d Length(ns)[BC] : %f [%d]", i, mROFrameLayerLength[i], mROFrameLayerLengthInBC[i]); + LOGF(info, "Strobe delay Layer %d (ns) : %f", i, mStrobeDelay); + LOGF(info, "Strobe length Layer %d (ns) : %f", i, mStrobeLength); + } + } else { + LOGF(info, "Readout Frame Length(ns) : %f", mROFrameLength); + LOGF(info, "Strobe delay (ns) : %f", mStrobeDelay); + LOGF(info, "Strobe length (ns) : %f", mStrobeLength); + } + LOGF(info, "Threshold (N electrons) : %d", mChargeThreshold); + LOGF(info, "Min N electrons to account : %d", mMinChargeToAccount); + LOGF(info, "Number of charge sharing steps : %d", mNSimSteps); + LOGF(info, "ELoss to N electrons factor : %e", mEnergyToNElectrons); + LOGF(info, "Noise level per pixel : %e", mNoisePerPixel); + LOGF(info, "Charge time-response:"); mSignalShape.print(); } diff --git a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx index 382fa769a94c7..b1a92e988968b 100644 --- a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx +++ b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx @@ -13,6 +13,7 @@ /// \brief Implementation of the ITS/MFT digitizer #include "DataFormatsITSMFT/Digit.h" +#include "Framework/Logger.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSMFTSimulation/DPLDigitizerParam.h" #include "ITSMFTSimulation/Digitizer.h" @@ -21,10 +22,11 @@ #include "DetectorsRaw/HBFUtils.h" #include +#include #include #include +#include #include -#include // for LOG using o2::itsmft::Digit; using o2::itsmft::Hit; @@ -48,24 +50,6 @@ void Digitizer::init() mChips[i].setDeadChanMap(mDeadChanMap); } } - // initializing for both collection tables - /*for (int i = 0; i < 2; i++) { - mAlpSimResp[i].initData(i); - }*/ - - // importing the charge collection tables - // (initialized while building O2) - auto file = TFile::Open(mResponseFile.data()); - if (!file) { - LOG(fatal) << "Cannot open response file " << mResponseFile; - } - /*std::string response = "response"; - for (int i=0; i<2; i++) { - response.append(std::to_string(i)); - mAlpSimResp[i] = *(o2::itsmft::AlpideSimResponse*)file->Get(response.data()); - }*/ - mAlpSimResp[0] = *(o2::itsmft::AlpideSimResponse*)file->Get("response0"); - mAlpSimResp[1] = *(o2::itsmft::AlpideSimResponse*)file->Get("response1"); // importing the parameters from DPLDigitizerParam.h auto& doptMFT = DPLDigitizerParam::Instance(); @@ -73,32 +57,32 @@ void Digitizer::init() // initializing response according to detector and back-bias value if (doptMFT.Vbb == 0.0) { // for MFT - mAlpSimRespMFT = mAlpSimResp; + mAlpSimRespMFT = mAlpSimResp[0]; LOG(info) << "Choosing Vbb=0V for MFT"; } else if (doptMFT.Vbb == 3.0) { - mAlpSimRespMFT = mAlpSimResp + 1; + mAlpSimRespMFT = mAlpSimResp[1]; LOG(info) << "Choosing Vbb=-3V for MFT"; } else { LOG(fatal) << "Invalid MFT back-bias value"; } if (doptITS.IBVbb == 0.0) { // for ITS Inner Barrel - mAlpSimRespIB = mAlpSimResp; + mAlpSimRespIB = mAlpSimResp[0]; LOG(info) << "Choosing Vbb=0V for ITS IB"; } else if (doptITS.IBVbb == 3.0) { - mAlpSimRespIB = mAlpSimResp + 1; + mAlpSimRespIB = mAlpSimResp[1]; LOG(info) << "Choosing Vbb=-3V for ITS IB"; } else { LOG(fatal) << "Invalid ITS Inner Barrel back-bias value"; } - if (doptITS.OBVbb == 0.0) { // for ITS Outter Barrel - mAlpSimRespOB = mAlpSimResp; + if (doptITS.OBVbb == 0.0) { // for ITS Outer Barrel + mAlpSimRespOB = mAlpSimResp[0]; LOG(info) << "Choosing Vbb=0V for ITS OB"; } else if (doptITS.OBVbb == 3.0) { - mAlpSimRespOB = mAlpSimResp + 1; + mAlpSimRespOB = mAlpSimResp[1]; LOG(info) << "Choosing Vbb=-3V for ITS OB"; } else { - LOG(fatal) << "Invalid ITS Outter Barrel back-bias value"; + LOG(fatal) << "Invalid ITS Outer Barrel back-bias value"; } mParams.print(); mIRFirstSampledTF = o2::raw::HBFUtils::Instance().getFirstSampledTFIR(); @@ -116,47 +100,53 @@ auto Digitizer::getChipResponse(int chipID) if (chipID < 432) { // in ITS Inner Barrel return mAlpSimRespIB; - } else { // in ITS Outter Barrel + } else { // in ITS Outer Barrel return mAlpSimRespOB; } } //_______________________________________________________________________ -void Digitizer::process(const std::vector* hits, int evID, int srcID) +void Digitizer::process(const std::vector* hits, int evID, int srcID, int layer) { // digitize single event, the time must have been set beforehand + // opt. apply a filter on the layer of the processed hits - LOG(info) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " - << srcID << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" - << " cont.mode: " << isContinuous() - << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; + LOG(debug) << "Digitizing " << mGeometry->getName() << ":" << layer << " hits of entry " << evID << " from source " + << srcID << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" + << " cont.mode: " << isContinuous() + << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; // is there something to flush ? if (mNewROFrame > mROFrameMin) { - fillOutputContainer(mNewROFrame - 1); // flush out all frame preceding the new one + fillOutputContainer(mNewROFrame - 1, layer); // flush out all frame preceding the new one } int nHits = hits->size(); std::vector hitIdx(nHits); std::iota(std::begin(hitIdx), std::end(hitIdx), 0); // sort hits to improve memory access - std::sort(hitIdx.begin(), hitIdx.end(), - [hits](auto lhs, auto rhs) { - return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID(); - }); - for (int i : hitIdx) { - processHit((*hits)[i], mROFrameMax, evID, srcID); + std::sort(hitIdx.begin(), hitIdx.end(), [hits](auto lhs, auto rhs) { + return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID(); + }); + for (int i : hitIdx | std::views::filter([&](int idx) { + if (layer < 0) { + return true; + } + return mGeometry->getLayer((*hits)[idx].GetDetectorID()) == layer; + })) { + processHit((*hits)[i], mROFrameMax, evID, srcID, layer); } + // in the triggered mode store digits after every MC event // TODO: in the real triggered mode this will not be needed, this is actually for the // single event processing only if (!mParams.isContinuous()) { - fillOutputContainer(mROFrameMax); + fillOutputContainer(mROFrameMax, layer); } } //_______________________________________________________________________ -void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) +void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt, int layer) { // assign event time in ns mEventTime = irt; @@ -179,13 +169,13 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) // this event is before the first RO mIsBeforeFirstRO = true; } else { - mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + mNewROFrame = nbc / mParams.getROFrameLengthInBC(layer); mIsBeforeFirstRO = false; } - LOG(info) << " NewROFrame " << mNewROFrame << " nbc " << nbc; + LOG(debug) << " NewROFrame " << mNewROFrame << " nbc " << nbc; // in continuous mode depends on starts of periodic readout frame - mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS; + mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC(layer)) * o2::constants::lhc::LHCBunchSpacingNS; } else { mNewROFrame = 0; } @@ -201,16 +191,14 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) } //_______________________________________________________________________ -void Digitizer::fillOutputContainer(uint32_t frameLast) +void Digitizer::fillOutputContainer(uint32_t frameLast, int layer) { // fill output with digits from min.cached up to requested frame, generating the noise beforehand - if (frameLast > mROFrameMax) { - frameLast = mROFrameMax; - } + frameLast = std::min(frameLast, mROFrameMax); // make sure all buffers for extra digits are created up to the maxFrame getExtraDigBuffer(mROFrameMax); - LOG(info) << "Filling " << mGeometry->getName() << " digits output for RO frames " << mROFrameMin << ":" + LOG(info) << "Filling " << mGeometry->getName() << " digits:" << layer << " output for RO frames " << mROFrameMin << ":" << frameLast; o2::itsmft::ROFRecord rcROF; @@ -222,7 +210,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) auto& extra = *(mExtraBuff.front().get()); for (auto& chip : mChips) { - if (chip.isDisabled()) { + if (chip.isDisabled() || (layer >= 0 && mGeometry->getLayer(chip.getChipIndex()) != layer)) { continue; } chip.addNoise(mROFrameMin, mROFrameMin, &mParams); @@ -254,7 +242,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) // finalize ROF record rcROF.setNEntries(mDigits->size() - rcROF.getFirstEntry()); // number of digits if (isContinuous()) { - rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC()); + rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC(layer)); } else { rcROF.getBCData() = mEventTime; // RSTODO do we need to add trigger delay? } @@ -269,10 +257,10 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) } //_______________________________________________________________________ -void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) +void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID, int lay) { // convert single hit to digits - int chipID = hit.GetDetectorID(); + auto chipID = hit.GetDetectorID(); auto& chip = mChips[chipID]; if (chip.isDisabled()) { LOG(debug) << "skip disabled chip " << chipID; @@ -302,14 +290,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } float tTot = mParams.getSignalShape().getMaxDuration(); // frame of the hit signal start wrt event ROFrame - int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv()); + int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv(lay)); // frame of the hit signal end wrt event ROFrame: in the triggered mode we read just 1 frame - uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv() : roFrameRel; + uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv(lay) : roFrameRel; int nFrames = roFrameRelMax + 1 - roFrameRel; uint32_t roFrameMax = mNewROFrame + roFrameRelMax; - if (roFrameMax > maxFr) { - maxFr = roFrameMax; // if signal extends beyond current maxFrame, increase the latter - } + maxFr = std::max(roFrameMax, maxFr); // if signal extends beyond current maxFrame, increase the latter // here we start stepping in the depth of the sensor to generate charge diffusion float nStepsInv = mParams.getNSimStepsInv(); @@ -350,17 +336,13 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } rowS -= AlpideRespSimMat::NPix / 2; rowE += AlpideRespSimMat::NPix / 2; - if (rowS < 0) { - rowS = 0; - } + rowS = std::max(rowS, 0); if (rowE >= Segmentation::NRows) { rowE = Segmentation::NRows - 1; } colS -= AlpideRespSimMat::NPix / 2; colE += AlpideRespSimMat::NPix / 2; - if (colS < 0) { - colS = 0; - } + colS = std::max(colS, 0); if (colE >= Segmentation::NCols) { colE = Segmentation::NCols - 1; } @@ -380,7 +362,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID const o2::itsmft::AlpideSimResponse* resp = getChipResponse(chipID); - // take into account that the AlpideSimResponse depth defintion has different min/max boundaries + // take into account that the AlpideSimResponse depth definition has different min/max boundaries // although the max should coincide with the surface of the epitaxial layer, which in the chip // local coordinates has Y = +SensorLayerThickness/2 @@ -397,7 +379,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID rowPrev = row; colPrev = col; } - bool flipCol, flipRow; + bool flipCol = false, flipRow = false; // note that response needs coordinates along column row (locX) (locZ) then depth (locY) auto rspmat = resp->getResponse(xyzLocS.X() - cRowPix, xyzLocS.Z() - cColPix, xyzLocS.Y(), flipRow, flipCol); @@ -407,12 +389,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } for (int irow = AlpideRespSimMat::NPix; irow--;) { - int rowDest = row + irow - AlpideRespSimMat::NPix / 2 - rowS; // destination row in the respMatrix + int rowDest = row + irow - (AlpideRespSimMat::NPix / 2) - rowS; // destination row in the respMatrix if (rowDest < 0 || rowDest >= rowSpan) { continue; } for (int icol = AlpideRespSimMat::NPix; icol--;) { - int colDest = col + icol - AlpideRespSimMat::NPix / 2 - colS; // destination column in the respMatrix + int colDest = col + icol - (AlpideRespSimMat::NPix / 2) - colS; // destination column in the respMatrix if (colDest < 0 || colDest >= colSpan) { continue; } @@ -444,35 +426,31 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID continue; } // - registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl); + registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl, lay); } } } //________________________________________________________________________________ void Digitizer::registerDigits(ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, - uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl) + uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl, int lay) { // Register digits for given pixel, accounting for the possible signal contribution to // multiple ROFrame. The signal starts at time tInROF wrt the start of provided roFrame // In every ROFrame we check the collected signal during strobe - float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start + float tStrobe = mParams.getStrobeDelay(lay) - tInROF; // strobe start wrt signal start for (int i = 0; i < nROF; i++) { uint32_t roFr = roFrame + i; - int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength()); - tStrobe += mParams.getROFrameLength(); // for the next ROF + int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength(lay)); + tStrobe += mParams.getROFrameLength(lay); // for the next ROF // discard too small contributions, they have no chance to produce a digit if (nEleROF < mParams.getMinChargeToAccount()) { continue; } - if (roFr > mEventROFrameMax) { - mEventROFrameMax = roFr; - } - if (roFr < mEventROFrameMin) { - mEventROFrameMin = roFr; - } + mEventROFrameMax = std::max(roFr, mEventROFrameMax); + mEventROFrameMin = std::min(roFr, mEventROFrameMin); auto key = chip.getOrderingKey(roFr, row, col); PreDigit* pd = chip.findDigit(key); if (!pd) { diff --git a/Detectors/ITSMFT/common/workflow/CMakeLists.txt b/Detectors/ITSMFT/common/workflow/CMakeLists.txt index 63cd8d6c0bcee..ead08c4422260 100644 --- a/Detectors/ITSMFT/common/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/common/workflow/CMakeLists.txt @@ -11,6 +11,8 @@ o2_add_library(ITSMFTWorkflow SOURCES src/ClusterReaderSpec.cxx + src/ClusterWriterSpec.cxx + src/ClustererSpec.cxx src/DigitWriterSpec.cxx src/DigitReaderSpec.cxx src/STFDecoderSpec.cxx diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h index 99318df1cd9d9..82e3890de7475 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h @@ -23,46 +23,51 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { +template class ClusterReader : public Task { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{(N == o2::detectors::DetID::ITS) ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + ClusterReader() = delete; - ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns = true, bool triggers = true); + ClusterReader(bool useMC, bool usePatterns = true, bool triggers = true); ~ClusterReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; protected: void connectTree(const std::string& filename); + template + void setBranchAddress(const std::string& base, Ptr& addr, int layer); + std::string getBranchName(const std::string& base, int index) const; - std::vector mClusROFRec, *mClusROFRecPtr = &mClusROFRec; - std::vector mClusterCompArray, *mClusterCompArrayPtr = &mClusterCompArray; - std::vector mPatternsArray, *mPatternsArrayPtr = &mPatternsArray; - o2::dataformats::MCTruthContainer mClusterMCTruth, *mClusterMCTruthPtr = &mClusterMCTruth; - std::vector mClusMC2ROFs, *mClusMC2ROFsPtr = &mClusMC2ROFs; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + std::array*, NLayers> mClusROFRec; + std::array*, NLayers> mClusterCompArray; + std::array*, NLayers> mPatternsArray; + std::array*, NLayers> mClusterMCTruth; + std::array*, NLayers> mClusMC2ROFs; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth + bool mUseMC = true; // use MC truth bool mUsePatterns = true; // send patterns bool mTriggerOut = true; // send dummy triggers vector - std::string mDetName = ""; - std::string mDetNameLC = ""; - std::string mFileName = ""; + std::string mDetName; + std::string mDetNameLC; + std::string mFileName; std::string mClusTreeName = "o2sim"; std::string mClusROFBranchName = "ClustersROF"; std::string mClusterPattBranchName = "ClusterPatt"; @@ -71,24 +76,18 @@ class ClusterReader : public Task std::string mClustMC2ROFBranchName = "ClustersMC2ROF"; }; -class ITSClusterReader : public ClusterReader +class ITSClusterReader : public ClusterReader { public: ITSClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::ITS, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginITS; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; -class MFTClusterReader : public ClusterReader +class MFTClusterReader : public ClusterReader { public: MFTClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::MFT, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginMFT; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; /// create a processor spec @@ -96,7 +95,6 @@ class MFTClusterReader : public ClusterReader framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITSMFT_CLUSTERREADER */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h similarity index 73% rename from Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h index 42b96786af27a..5ae371e7e09c4 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h @@ -11,21 +11,19 @@ /// @file ClusterWriterSpec.h -#ifndef O2_ITS_CLUSTERWRITER -#define O2_ITS_CLUSTERWRITER +#ifndef O2_ITSMFT_CLUSTERWRITER +#define O2_ITSMFT_CLUSTERWRITER #include "Framework/DataProcessorSpec.h" -namespace o2 -{ -namespace its +namespace o2::itsmft { -/// create a processor spec -/// write ITS clusters to ROOT file +template framework::DataProcessorSpec getClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC); -} // namespace its -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITS_CLUSTERWRITER */ diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h similarity index 64% rename from Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h index f0a763597ff74..b6ebc282c2a27 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h @@ -11,24 +11,27 @@ /// @file ClustererSpec.h -#ifndef O2_MFT_CLUSTERERDPL_H_ -#define O2_MFT_CLUSTERERDPL_H_ +#ifndef O2_ITSMFT_CLUSTERERDPL_H_ +#define O2_ITSMFT_CLUSTERERDPL_H_ -#include #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ITSMFTReconstruction/Clusterer.h" +#include "ITSMFTBase/DPLAlpideParam.h" using namespace o2::framework; -namespace o2 -{ -namespace mft +namespace o2::itsmft { +template class ClustererDPL : public Task { + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + public: ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} ~ClustererDPL() override = default; @@ -39,20 +42,19 @@ class ClustererDPL : public Task private: void updateTimeDependentParams(ProcessingContext& pc); - int mState = 0; + std::string mDetName; bool mUseMC = true; bool mUseClusterDictionary = true; int mNThreads = 1; - std::unique_ptr mFile = nullptr; std::unique_ptr mClusterer = nullptr; std::shared_ptr mGGCCDBRequest; + int mLayers{NLayers}; + std::vector mFilter; }; -/// create a processor spec -/// run MFT cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); +framework::DataProcessorSpec getITSClustererSpec(bool useMC); +framework::DataProcessorSpec getMFTClustererSpec(bool useMC); -} // namespace mft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_MFT_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h index e655e05842d71..348ba76468144 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h @@ -16,6 +16,7 @@ #include "TFile.h" #include "TTree.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -34,64 +35,67 @@ namespace o2 namespace itsmft { +template class DigitReader : public Task { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::getNLayers()}; + static constexpr int RLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? NLayers : 1; + DigitReader() = delete; - DigitReader(o2::detectors::DetID id, bool useMC, bool useCalib, bool triggerOut); + DigitReader(bool useMC, bool useCalib, bool triggerOut); ~DigitReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; protected: void connectTree(const std::string& filename); + template + void setBranchAddress(const std::string& base, Ptr& addr, int layer = -1); + std::string getBranchName(const std::string& base, int index); - std::vector mDigits, *mDigitsPtr = &mDigits; + std::array*, NLayers> mDigits; std::vector mCalib, *mCalibPtr = &mCalib; - std::vector mDigROFRec, *mDigROFRecPtr = &mDigROFRec; - std::vector mDigMC2ROFs, *mDigMC2ROFsPtr = &mDigMC2ROFs; - o2::dataformats::ConstMCTruthContainer mConstLabels; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + std::array*, NLayers> mDigROFRec; + std::array*, NLayers> mDigMC2ROFs; + std::array, NLayers> mConstLabels; + std::array mPLabels; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth - bool mUseCalib = true; // send calib data - bool mTriggerOut = true; // send dummy triggers vector + bool mUseMC = true; // use MC truth + bool mUseCalib = true; // send calib data + bool mTriggerOut = true; // send dummy triggers vector bool mUseIRFrames = false; // selected IRFrames modes int mROFBiasInBC = 0; int mROFLengthInBC = 0; int mNRUs = 0; - std::string mDetName = ""; - std::string mDetNameLC = ""; - std::string mFileName = ""; + std::string mDetName; + std::string mDetNameLC; + std::string mFileName; std::string mDigTreeName = "o2sim"; std::string mDigitBranchName = "Digit"; - std::string mDigROFBranchName = "DigitROF"; + std::string mDigitROFBranchName = "DigitROF"; std::string mCalibBranchName = "Calib"; - std::string mDigtMCTruthBranchName = "DigitMCTruth"; - std::string mDigtMC2ROFBranchName = "DigitMC2ROF"; + std::string mDigitMCTruthBranchName = "DigitMCTruth"; + std::string mDigitMC2ROFBranchName = "DigitMC2ROF"; }; -class ITSDigitReader : public DigitReader +class ITSDigitReader : public DigitReader { public: ITSDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(o2::detectors::DetID::ITS, useMC, useCalib, useTriggers) - { - mOrigin = o2::header::gDataOriginITS; - } + : DigitReader(useMC, useCalib, useTriggers) {} }; -class MFTDigitReader : public DigitReader +class MFTDigitReader : public DigitReader { public: MFTDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(o2::detectors::DetID::MFT, useMC, useCalib, useTriggers) - { - mOrigin = o2::header::gDataOriginMFT; - } + : DigitReader(useMC, useCalib, useTriggers) {} }; /// create a processor spec diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h index 4ed4e99f4b6f8..a64f2bf8c063c 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h @@ -32,7 +32,7 @@ namespace itsmft class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits = false); + EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits = false, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -60,7 +60,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h index c10ae16c95a3e..588cae6339489 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h @@ -30,7 +30,7 @@ namespace itsmft class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR); + EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -48,7 +48,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index ea906056c7898..bc6418a077810 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -12,15 +12,16 @@ /// @file ClusterReaderSpec.cxx #include +#include -#include "TTree.h" +#include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" -#include #include "CommonUtils/NameConf.h" using namespace o2::framework; @@ -31,45 +32,48 @@ namespace o2 namespace itsmft { -ClusterReader::ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns, bool triggerOut) +template +ClusterReader::ClusterReader(bool useMC, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) { - assert(id == o2::detectors::DetID::ITS || id == o2::detectors::DetID::MFT); - mDetNameLC = mDetName = id.getName(); - mUseMC = useMC; - mUsePatterns = usePatterns; - mTriggerOut = triggerOut; std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); + + mClusROFRec.fill(nullptr); + mClusterCompArray.fill(nullptr); + mPatternsArray.fill(nullptr); + mClusterMCTruth.fill(nullptr); + mClusMC2ROFs.fill(nullptr); } -void ClusterReader::init(InitContext& ic) +template +void ClusterReader::init(InitContext& ic) { mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get((mDetNameLC + "-cluster-infile").c_str())); connectTree(mFileName); } -void ClusterReader::run(ProcessingContext& pc) +template +void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << mDetName << "ClusterReader pushes " << mClusROFRec.size() << " ROFRecords," - << mClusterCompArray.size() << " compact clusters at entry " << ent; - - // This is a very ugly way of providing DataDescription, which anyway does not need to contain detector name. - // To be fixed once the names-definition class is ready - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSROF", 0}, mClusROFRec); - pc.outputs().snapshot(Output{mOrigin, "COMPCLUSTERS", 0}, mClusterCompArray); - if (mUsePatterns) { - pc.outputs().snapshot(Output{mOrigin, "PATTERNS", 0}, mPatternsArray); - } - if (mUseMC) { - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMCTR", 0}, mClusterMCTruth); - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMC2ROF", 0}, mClusMC2ROFs); + + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + LOG(info) << mDetName << "ClusterReader:" << iLayer << " pushes " << mClusROFRec[iLayer]->size() << " ROFRecords, " << mClusterCompArray[iLayer]->size() << " compact clusters at entry " << ent; + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, *mClusROFRec[iLayer]); + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, *mClusterCompArray[iLayer]); + if (mUsePatterns) { + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, *mPatternsArray[iLayer]); + } + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *mClusterMCTruth[iLayer]); + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, *mClusMC2ROFs[iLayer]); + } } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); @@ -77,7 +81,8 @@ void ClusterReader::run(ProcessingContext& pc) } } -void ClusterReader::connectTree(const std::string& filename) +template +void ClusterReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); @@ -85,70 +90,89 @@ void ClusterReader::connectTree(const std::string& filename) mTree.reset((TTree*)mFile->Get(mClusTreeName.c_str())); assert(mTree); - mTree->SetBranchAddress((mDetName + mClusROFBranchName).c_str(), &mClusROFRecPtr); - mTree->SetBranchAddress((mDetName + mClusterCompBranchName).c_str(), &mClusterCompArrayPtr); - if (mUsePatterns) { - mTree->SetBranchAddress((mDetName + mClusterPattBranchName).c_str(), &mPatternsArrayPtr); - } - if (mUseMC) { - if (mTree->GetBranch((mDetName + mClustMCTruthBranchName).c_str()) && - mTree->GetBranch((mDetName + mClustMC2ROFBranchName).c_str())) { - mTree->SetBranchAddress((mDetName + mClustMCTruthBranchName).c_str(), &mClusterMCTruthPtr); - mTree->SetBranchAddress((mDetName + mClustMC2ROFBranchName).c_str(), &mClusMC2ROFsPtr); - } else { - LOG(info) << "MC-truth is missing"; - mUseMC = false; + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + setBranchAddress(mClusROFBranchName, mClusROFRec[iLayer], iLayer); + setBranchAddress(mClusterCompBranchName, mClusterCompArray[iLayer], iLayer); + if (mUsePatterns) { + setBranchAddress(mClusterPattBranchName, mPatternsArray[iLayer], iLayer); + } + if (mUseMC) { + if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str()) && + mTree->GetBranch(getBranchName(mClustMC2ROFBranchName, iLayer).c_str())) { + setBranchAddress(mClustMCTruthBranchName, mClusterMCTruth[iLayer], iLayer); + setBranchAddress(mClustMC2ROFBranchName, mClusMC2ROFs[iLayer], iLayer); + } else { + LOG(info) << "MC-truth is missing"; + mUseMC = false; + } } } LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; } -DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +template +std::string ClusterReader::getBranchName(const std::string& base, int index) const +{ + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return mDetName + base + "_" + std::to_string(index); + } + return mDetName + base; +} + +template +template +void ClusterReader::setBranchAddress(const std::string& base, Ptr& addr, int layer) { - std::vector outputSpec; - outputSpec.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); + const auto name = getBranchName(base, layer); + if (Int_t ret = mTree->SetBranchAddress(name.c_str(), &addr); ret != 0) { + LOGP(fatal, "failed to set branch address for {} ret={}", name, ret); } - if (useMC) { - outputSpec.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); +} + +namespace +{ +template +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool usePatterns, bool triggerOut) +{ + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < ((o2::itsmft::DPLAlpideParam::supportsStaggering()) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { + outputs.emplace_back(detOrig, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + if (usePatterns) { + outputs.emplace_back(detOrig, "PATTERNS", iLayer, Lifetime::Timeframe); + } + if (mctruth) { + outputs.emplace_back(detOrig, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } } if (triggerOut) { - outputSpec.emplace_back("ITS", "PHYSTRIG", 0, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "PHYSTRIG", 0, Lifetime::Timeframe); } + return outputs; +} +} // namespace + +DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +{ return DataProcessorSpec{ - "its-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "its-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("ITS", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"its-cluster-infile", VariantType::String, "o2clus_its.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) { - std::vector outputSpec; - outputSpec.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - } - if (useMC) { - outputSpec.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - if (triggerOut) { - outputSpec.emplace_back("MFT", "PHYSTRIG", 0, Lifetime::Timeframe); - } return DataProcessorSpec{ - "mft-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "mft-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("MFT", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"mft-cluster-infile", VariantType::String, "mftclusters.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..c1900c346133b --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,107 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterWriterSpec.cxx + +#include +#include +#include +#include +#include + +#include "Framework/ConcreteDataMatcher.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2::itsmft +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using CompClusType = std::vector; +using PatternsType = std::vector; +using ROFrameRType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; +using ROFRecLblT = std::vector; +using namespace o2::header; + +template +DataProcessorSpec getClusterWriterSpec(bool useMC) +{ + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr int NLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + const auto detName = Origin.as(); + // Spectators for logging + auto compClusterSizes = std::make_shared>(); + auto compClustersSizeGetter = [compClusterSizes](CompClusType const& compClusters, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*compClusterSizes)[dh->subSpecification] = compClusters.size(); + }; + auto logger = [detName, compClusterSizes](std::vector const& rofs, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + const auto i = dh->subSpecification; + LOG(info) << detName << "ClusterWriter:" << i << " pulled " << (*compClusterSizes)[i] << " clusters, in " << rofs.size() << " RO frames"; + }; + auto getIndex = [](DataRef const& ref) -> size_t { + auto const* dh = DataRefUtils::getHeader(ref); + return static_cast(dh->subSpecification); + }; + auto getName = [](std::string base, size_t index) -> std::string { + if constexpr (DPLAlpideParam::supportsStaggering()) { + return base += "_" + std::to_string(index); + } + return base; + }; + auto detNameLC = detName; + std::transform(detNameLC.begin(), detNameLC.end(), detNameLC.begin(), [](unsigned char c) { return std::tolower(c); }); + return MakeRootTreeWriterSpec(std::format("{}-cluster-writer", detNameLC).c_str(), + (o2::detectors::DetID::ITS == N) ? "o2clus_its.root" : "mftclusters.root", + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = std::format("Tree with {} clusters", detName)}, + BranchDefinition{InputSpec{"compclus", ConcreteDataTypeMatcher{Origin, "COMPCLUSTERS"}}, + (detName + "ClusterComp").c_str(), "compact-cluster-branch", + NLayers, + compClustersSizeGetter, + getIndex, + getName}, + BranchDefinition{InputSpec{"patterns", ConcreteDataTypeMatcher{Origin, "PATTERNS"}}, + (detName + "ClusterPatt").c_str(), "cluster-pattern-branch", + NLayers, + getIndex, + getName}, + BranchDefinition{InputSpec{"ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSROF"}}, + (detName + "ClustersROF").c_str(), "cluster-rof-branch", + NLayers, + logger, + getIndex, + getName}, + BranchDefinition{InputSpec{"labels", ConcreteDataTypeMatcher{Origin, "CLUSTERSMCTR"}}, + (detName + "ClusterMCTruth").c_str(), "cluster-label-branch", + (useMC ? NLayers : 0), + getIndex, + getName}, + BranchDefinition{InputSpec{"MC2ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSMC2ROF"}}, + (detName + "ClustersMC2ROF").c_str(), "cluster-mc2rof-branch", + (useMC ? NLayers : 0), + getIndex, + getName})(); +} + +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..0b6bb44ee78c8 --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -0,0 +1,325 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClustererSpec.cxx + +#include + +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsITSMFT/Digit.h" +#include "Framework/InputRecordWalker.h" +#include "ITSMFTReconstruction/ChipMappingMFT.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsParameters/GRPObject.h" +#include "ITSMFTReconstruction/DigitPixelReader.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "ITSMFTReconstruction/ClustererParam.h" + +namespace o2::itsmft +{ + +template +void ClustererDPL::init(InitContext& ic) +{ + mClusterer = std::make_unique(); + mClusterer->setNChips((N == o2::detectors::DetID::ITS) ? o2::itsmft::ChipMappingITS::getNChips() : o2::itsmft::ChipMappingMFT::getNChips()); + mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mNThreads = std::max(1, ic.options().get("nthreads")); + mDetName = Origin.as(); + + // prepare data filter + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + mFilter.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("ROframe", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mUseMC) { + mFilter.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } +} + +template +void ClustererDPL::run(ProcessingContext& pc) +{ + updateTimeDependentParams(pc); + + // filter input and compose + std::array, NLayers> digits; + std::array, NLayers> rofs; + std::array, NLayers> labelsbuffer; + std::array, NLayers> mc2rofs; + for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) { + auto const* dh = DataRefUtils::getHeader(ref); + if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) { + digits[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"ROframe", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}})) { + rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"labels", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}})) { + labelsbuffer[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"MC2ROframes", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}})) { + mc2rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + } + + // query the first orbit in this TF + const auto firstTForbit = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, firstTForbit); + const auto& par = DPLAlpideParam::Instance(); + + // process received inputs + uint64_t nClusters{0}; + TStopwatch sw; + o2::itsmft::DigitPixelReader reader; + for (uint32_t iLayer{0}; iLayer < NLayers; ++iLayer) { + int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + sw.Start(); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; + + mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer)); + o2::dataformats::ConstMCTruthContainerView labels(labelsbuffer[iLayer]); + reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(layer)); + reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking + reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(layer)); + reader.setDigits(digits[iLayer]); + reader.setROFRecords(rofs[iLayer]); + if (mUseMC) { + reader.setMC2ROFRecords(mc2rofs[iLayer]); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << labels.getNElements() << " labels "; + reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); + } + reader.init(); + std::vector clusCompVec; + std::vector clusROFVec; + std::vector clusPattVec; + + std::unique_ptr> clusterLabels; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); + + // ensure that the rof output is continuous + size_t nROFs = clusROFVec.size(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF(); + if (nROFsTF != clusROFVec.size()) { + // it can happen that in the digitization rofs without contributing hits are skipped + // however downstream consumers of the clusters cannot know apriori the time structure + // the cluster rofs do not account for the bias so it will start always at BC=0 + // if we receive more cluster rofs then there supposed to be, do not throw away this data + // the clusterer should be blind to this! + const size_t nROFsLayer = std::max((size_t)nROFsTF, clusROFVec.size()); + std::vector expClusRofVec(nROFsLayer); + for (int iROF{0}; iROF < nROFsLayer; ++iROF) { + auto& rof = expClusRofVec[iROF]; + int orb = iROF * par.getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + firstTForbit; + int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + uint32_t prevEntry{0}; + for (const auto& rof : clusROFVec) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const int irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); + auto& expROF = expClusRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + } + } + int prevFirst{0}; + for (auto& rof : expClusRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + nROFs = expClusRofVec.size(); + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); + } else { + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, clusROFVec); + } + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, clusCompVec); + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, clusPattVec); + + nClusters += clusCompVec.size(); + + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *clusterLabels); // at the moment requires snapshot + std::vector clusterMC2ROframes(mc2rofs[iLayer].size()); + for (int i = mc2rofs[iLayer].size(); i--;) { + clusterMC2ROframes[i] = mc2rofs[iLayer][i]; // Simply, replicate it from digits ? + } + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, clusterMC2ROframes); + } + reader.reset(); + + // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF + // -> consider recalculationg maxROF + sw.Stop(); + LOG(info) << mDetName << "Clusterer:" << layer << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; + } + + LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters"; +} + +///_______________________________________ +template +void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + pc.inputs().get("cldict"); // just to trigger the finaliseCCDB + pc.inputs().get*>("alppar"); + pc.inputs().get*>("cluspar"); + mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(N)); + // settings for the fired pixel overflow masking + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + const auto& clParams = o2::itsmft::ClustererParam::Instance(); + if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { + LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); + } + mClusterer->setDropHugeClusters(clParams.dropHugeClusters); + auto nbc = clParams.maxBCDiffToMaskBias; + nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); + mClusterer->setMaxBCSeparationToMask(nbc); + mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); + // Squasher + int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC + mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); + int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. + if (clParams.maxSOTMUS > 0 && rofBC > 0) { + nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing + } + mClusterer->setMaxROFDepthToSquash(nROFsToSquash); + if constexpr (DPLAlpideParam::supportsStaggering()) { + if (mClusterer->isContinuousReadOut()) { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer)); + mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0); + } + } + } + mClusterer->print(false); + } + // we may have other params which need to be queried regularly +} + +///_______________________________________ +template +void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); + if (mUseClusterDictionary) { + mClusterer->setDictionary((const TopologyDictionary*)obj); + } + return; + } + // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSPARAM", 0)) { + LOG(info) << "Cluster param updated"; + const auto& par = o2::itsmft::ClustererParam::Instance(); + par.printKeyValues(); + return; + } +} + +namespace +{ +template +DataProcessorSpec getClustererSpec(bool useMC) +{ + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + std::vector inputs; + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("ROframes", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + inputs.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Calib/ClusterDictionary")); + inputs.emplace_back("cluspar", Origin, "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/ClustererParam")); + inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/AlpideParam")); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + outputs.emplace_back(Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "PATTERNS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(Origin, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + return DataProcessorSpec{ + .name = (N == o2::detectors::DetID::ITS) ? "its-clusterer" : "mft-clusterer", + .inputs = inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC)}, + .options = Options{ + {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, + {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; +} +} // namespace + +framework::DataProcessorSpec getITSClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +framework::DataProcessorSpec getMFTClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx index 3c7a86fe173d6..ec86da4833a0d 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx @@ -11,6 +11,7 @@ /// @file DigitReaderSpec.cxx +#include #include #include "TTree.h" @@ -39,25 +40,28 @@ namespace o2 namespace itsmft { -DigitReader::DigitReader(o2::detectors::DetID id, bool useMC, bool useCalib, bool triggerOut) +template +DigitReader::DigitReader(bool useMC, bool useCalib, bool triggerOut) : mUseMC(useMC), mUseCalib(useCalib), mTriggerOut(triggerOut), mDetNameLC(mDetName = ID.getName()), mDigTreeName("o2sim") { - assert(id == o2::detectors::DetID::ITS || id == o2::detectors::DetID::MFT); - mDetNameLC = mDetName = id.getName(); - mDigTreeName = "o2sim"; - mDigitBranchName = mDetName + mDigitBranchName; - mDigROFBranchName = mDetName + mDigROFBranchName; + mDigitROFBranchName = mDetName + mDigitROFBranchName; mCalibBranchName = mDetName + mCalibBranchName; - mDigtMCTruthBranchName = mDetName + mDigtMCTruthBranchName; - mDigtMC2ROFBranchName = mDetName + mDigtMC2ROFBranchName; - mTriggerOut = triggerOut; - mUseMC = useMC; - mUseCalib = useCalib; + mDigitMCTruthBranchName = mDetName + mDigitMCTruthBranchName; + mDigitMC2ROFBranchName = mDetName + mDigitMC2ROFBranchName; + std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); + + for (uint32_t i = 0; i < NLayers; ++i) { + mDigits[i] = nullptr; + mDigROFRec[i] = nullptr; + mDigMC2ROFs[i] = nullptr; + mPLabels[i] = nullptr; + } } -void DigitReader::init(InitContext& ic) +template +void DigitReader::init(InitContext& ic) { mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get((mDetNameLC + "-digit-infile").c_str())); @@ -67,23 +71,23 @@ void DigitReader::init(InitContext& ic) connectTree(mFileName); } -void DigitReader::run(ProcessingContext& pc) +template +void DigitReader::run(ProcessingContext& pc) { const auto& tinfo = pc.services().get(); + const auto& alpideParam = o2::itsmft::DPLAlpideParam::Instance(); if (tinfo.globalRunNumberChanged && mUseIRFrames) { // new run is starting: 1st call // TODO: we have to find a way define CCDBInput for IRFrames mode only using DPL fetcher auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); auto rlim = ccdb.getRunDuration(tinfo.runNumber); long ts = (rlim.first + rlim.second) / 2; - if (mOrigin == o2::header::gDataOriginITS) { + if constexpr (N == o2::detectors::DetID::ITS) { ccdb.getForTimeStamp>("ITS/Config/AlpideParam", ts); - const auto& alpideParam = o2::itsmft::DPLAlpideParam::Instance(); mROFBiasInBC = alpideParam.roFrameBiasInBC; mROFLengthInBC = alpideParam.roFrameLengthInBC; mNRUs = o2::itsmft::ChipMappingITS::getNRUs(); } else { ccdb.getForTimeStamp>("MFT/Config/AlpideParam", ts); - const auto& alpideParam = o2::itsmft::DPLAlpideParam::Instance(); mROFBiasInBC = alpideParam.roFrameBiasInBC; mROFLengthInBC = alpideParam.roFrameLengthInBC; mNRUs = o2::itsmft::ChipMappingMFT::getNRUs(); @@ -93,38 +97,37 @@ void DigitReader::run(ProcessingContext& pc) if (mUseIRFrames) { irFrames = pc.inputs().get>("driverInfo"); } - static o2::dataformats::IOMCTruthContainerView* plabels = nullptr; - if (mUseMC && !plabels) { - mTree->SetBranchAddress(mDigtMCTruthBranchName.c_str(), &plabels); - } - auto ent = mTree->GetReadEntry(); + auto ent = mTree->GetReadEntry(); if (!mUseIRFrames) { ent++; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << mDetName << "DigitReader pushes " << mDigROFRec.size() << " ROFRecords, " << mDigits.size() << " digits at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mDigROFRec); - pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); + for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { + LOG(info) << mDetName << "DigitReader:" << iLayer << " pushes " << mDigROFRec[iLayer]->size() << " ROFRecords, " << mDigits[iLayer]->size() << " digits at entry " << ent; + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, *mDigROFRec[iLayer]); + pc.outputs().snapshot(Output{Origin, "DIGITS", iLayer}, *mDigits[iLayer]); + if (mUseMC) { + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", iLayer}); + mPLabels[iLayer]->copyandflatten(sharedlabels); + delete mPLabels[iLayer]; + mPLabels[iLayer] = nullptr; + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, *mDigMC2ROFs[iLayer]); + } + } if (mUseCalib) { - pc.outputs().snapshot(Output{mOrigin, "GBTCALIB", 0}, mCalib); + pc.outputs().snapshot(Output{Origin, "GBTCALIB", 0}, mCalib); } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); - } - if (mUseMC) { - auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); - plabels->copyandflatten(sharedlabels); - delete plabels; - plabels = nullptr; - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mDigMC2ROFs); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } } else { // need to select particulars IRs range, presumably from the same tree entry + // TODO implement for staggering std::vector digitsSel; std::vector calibSel; std::vector digROFRecSel; @@ -144,33 +147,33 @@ void DigitReader::run(ProcessingContext& pc) // do we need to read a new entry? if (ent > mTree->GetReadEntry()) { if (mUseMC) { - delete plabels; - plabels = nullptr; - mConstLabels.clear(); - mTree->SetBranchAddress(mDigtMCTruthBranchName.c_str(), &plabels); + delete mPLabels[0]; + mPLabels[0] = nullptr; + mConstLabels[0].clear(); + mTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mPLabels[0]); } mTree->GetEntry(ent); if (mUseMC) { - plabels->copyandflatten(mConstLabels); - delete plabels; - plabels = nullptr; + mPLabels[0]->copyandflatten(mConstLabels[0]); + delete mPLabels[0]; + mPLabels[0] = nullptr; } } std::vector rofOld2New; - rofOld2New.resize(mDigROFRec.size(), -1); + rofOld2New.resize(mDigROFRec[0]->size(), -1); - if (mDigROFRec.front().getBCData() <= irMax && (mDigROFRec.back().getBCData() + mROFLengthInBC - 1) >= irMin) { // there is an overlap - for (int irof = 0; irof < (int)mDigROFRec.size(); irof++) { - const auto& rof = mDigROFRec[irof]; + if (mDigROFRec[0]->front().getBCData() <= irMax && (mDigROFRec[0]->back().getBCData() + mROFLengthInBC - 1) >= irMin) { // there is an overlap + for (int irof = 0; irof < (int)mDigROFRec[0]->size(); irof++) { + const auto& rof = mDigROFRec[0]->at(irof); if (irfSel.check({rof.getBCData(), rof.getBCData() + mROFLengthInBC - 1}) != -1) { rofOld2New[irof] = (int)digROFRecSel.size(); LOGP(debug, "Adding selected ROF {}", rof.getBCData().asString()); digROFRecSel.push_back(rof); int offs = digitsSel.size(); digROFRecSel.back().setFirstEntry(offs); - std::copy(mDigits.begin() + rof.getFirstEntry(), mDigits.begin() + rof.getFirstEntry() + rof.getNEntries(), std::back_inserter(digitsSel)); + std::copy(mDigits[0]->begin() + rof.getFirstEntry(), mDigits[0]->begin() + rof.getFirstEntry() + rof.getNEntries(), std::back_inserter(digitsSel)); for (int id = 0; id < rof.getNEntries(); id++) { // copy MC info - digitLabelsSel.addElements(id + offs, mConstLabels.getLabels(id + rof.getFirstEntry())); + digitLabelsSel.addElements(id + offs, mConstLabels[0].getLabels(id + rof.getFirstEntry())); } if (mCalib.size() >= size_t((irof + 1) * mNRUs)) { std::copy(mCalib.begin() + irof * mNRUs, mCalib.begin() + (irof + 1) * mNRUs, std::back_inserter(calibSel)); @@ -179,7 +182,7 @@ void DigitReader::run(ProcessingContext& pc) } } if (mUseMC) { - digMC2ROFsSel = mDigMC2ROFs; + digMC2ROFsSel = *mDigMC2ROFs[0]; for (auto& mc2rof : digMC2ROFsSel) { if (mc2rof.rofRecordID < 0) { continue; // did not contribute even to the original data @@ -198,26 +201,26 @@ void DigitReader::run(ProcessingContext& pc) mc2rof.maxROF = mx; } } - if (mDigROFRec.back().getBCData() + mROFLengthInBC - 1 < irMax) { // need to check the next entry + if (mDigROFRec[0]->back().getBCData() + mROFLengthInBC - 1 < irMax) { // need to check the next entry ent++; continue; } break; // push collected data } } - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, digROFRecSel); - pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, digitsSel); + pc.outputs().snapshot(Output{Origin, "DIGITSROF", 0}, digROFRecSel); + pc.outputs().snapshot(Output{Origin, "DIGITS", 0}, digitsSel); if (mUseCalib) { - pc.outputs().snapshot(Output{mOrigin, "GBTCALIB", 0}, calibSel); + pc.outputs().snapshot(Output{Origin, "GBTCALIB", 0}, calibSel); } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mUseMC) { - auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", 0}); digitLabelsSel.flatten_to(sharedlabels); - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, digMC2ROFsSel); + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", 0}, digMC2ROFsSel); } if (!irFrames.size() || irFrames.back().isLast()) { @@ -227,77 +230,99 @@ void DigitReader::run(ProcessingContext& pc) } } -void DigitReader::connectTree(const std::string& filename) +template +void DigitReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); assert(mFile && !mFile->IsZombie()); mTree.reset((TTree*)mFile->Get(mDigTreeName.c_str())); assert(mTree); - - mTree->SetBranchAddress(mDigROFBranchName.c_str(), &mDigROFRecPtr); - mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsPtr); + for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { + setBranchAddress(mDigitROFBranchName, mDigROFRec[iLayer], iLayer); + setBranchAddress(mDigitBranchName, mDigits[iLayer], iLayer); + if (mUseMC) { + if (!mTree->GetBranch(getBranchName(mDigitMC2ROFBranchName, iLayer).c_str()) || !mTree->GetBranch(getBranchName(mDigitMCTruthBranchName, iLayer).c_str())) { + throw std::runtime_error("MC data requested but not found in the tree"); + } + setBranchAddress(mDigitMC2ROFBranchName, mDigMC2ROFs[iLayer], iLayer); + if (!mPLabels[iLayer]) { + setBranchAddress(mDigitMCTruthBranchName, mPLabels[iLayer], iLayer); + } + } + } if (mUseCalib) { if (!mTree->GetBranch(mCalibBranchName.c_str())) { throw std::runtime_error("GBT calibration data requested but not found in the tree"); } - mTree->SetBranchAddress(mCalibBranchName.c_str(), &mCalibPtr); - } - if (mUseMC) { - if (!mTree->GetBranch(mDigtMC2ROFBranchName.c_str()) || !mTree->GetBranch(mDigtMCTruthBranchName.c_str())) { - throw std::runtime_error("MC data requested but not found in the tree"); - } - mTree->SetBranchAddress(mDigtMC2ROFBranchName.c_str(), &mDigMC2ROFsPtr); + setBranchAddress(mCalibBranchName, mCalibPtr); } LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; } -DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +template +std::string DigitReader::getBranchName(const std::string& base, int index) { - std::vector outputSpec; - outputSpec.emplace_back("ITS", "DIGITS", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "DIGITSROF", 0, Lifetime::Timeframe); - if (useCalib) { - outputSpec.emplace_back("ITS", "GBTCALIB", 0, Lifetime::Timeframe); + if constexpr (!o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return base; } - if (useMC) { - outputSpec.emplace_back("ITS", "DIGITSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe); + return base + "_" + std::to_string(index); +} + +template +template +void DigitReader::setBranchAddress(const std::string& base, Ptr& addr, int layer) +{ + const auto name = getBranchName(base, layer); + if (Int_t ret = mTree->SetBranchAddress(name.c_str(), &addr); ret != 0) { + LOGP(fatal, "failed to set branch address for {} ret={}", name, ret); } - if (useTriggers) { - outputSpec.emplace_back("ITS", "PHYSTRIG", 0, Lifetime::Timeframe); +} + +namespace +{ +template +std::vector makeOutChannels(bool mctruth, bool useCalib) +{ + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + std::vector outputs; + static constexpr int RLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (int iLayer = 0; iLayer < RLayers; ++iLayer) { + outputs.emplace_back(Origin, "DIGITS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mctruth) { + outputs.emplace_back(Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + } } + if (useCalib) { + outputs.emplace_back(Origin, "GBTCALIB", 0, Lifetime::Timeframe); + } + outputs.emplace_back(Origin, "PHYSTRIG", 0, Lifetime::Timeframe); + return outputs; +} +} // namespace + +DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +{ return DataProcessorSpec{ - "its-digit-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, useCalib)}, - Options{ + .name = "its-digit-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels(useMC, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .options = Options{ {"its-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } DataProcessorSpec getMFTDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) { - std::vector outputSpec; - outputSpec.emplace_back("MFT", "DIGITS", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "DIGITSROF", 0, Lifetime::Timeframe); - if (useCalib) { - outputSpec.emplace_back("MFT", "GBTCALIB", 0, Lifetime::Timeframe); - } - if (useMC) { - outputSpec.emplace_back("MFT", "DIGITSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - } - if (useTriggers) { - outputSpec.emplace_back("MFT", "PHYSTRIG", 0, Lifetime::Timeframe); - } return DataProcessorSpec{ - "mft-digit-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, useCalib)}, - Options{ + .name = "mft-digit-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels(useMC, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .options = Options{ {"mft-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx index 3a06d106ceb1f..c4f1e336180c7 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx @@ -12,6 +12,9 @@ /// @brief Processor spec for a ROOT file writer for ITSMFT digits #include "ITSMFTWorkflow/DigitWriterSpec.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/DataRef.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" @@ -39,14 +42,24 @@ using MCCont = o2::dataformats::ConstMCTruthContainer; /// create the processor spec /// describing a processor receiving digits for ITS/MFT and writing them to file -DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::header::DataOrigin detOrig, o2::detectors::DetID detId) +template +DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) { - std::string detStr = o2::detectors::DetID::getName(detId); + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr int NLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + std::string detStr = o2::detectors::DetID::getName(N); std::string detStrL = dec ? "o2_" : ""; // for decoded digits prepend by o2 detStrL += detStr; std::transform(detStrL.begin(), detStrL.end(), detStrL.begin(), ::tolower); - auto logger = [](std::vector const& inDigits) { - LOG(info) << "RECEIVED DIGITS SIZE " << inDigits.size(); + auto digitSizes = std::make_shared>(); + auto digitSizeGetter = [digitSizes](std::vector const& inDigits, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*digitSizes)[dh->subSpecification] = inDigits.size(); + }; + auto rofSizes = std::make_shared>(); + auto rofSizeGetter = [rofSizes](std::vector const& inROFs, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*rofSizes)[dh->subSpecification] = inROFs.size(); }; // the callback to be set as hook for custom action when the writer is closed @@ -71,9 +84,11 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::hea // handler for labels // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. // We therefore convert it to a special split class. - auto fillLabels = [](TBranch& branch, std::vector const& labelbuffer, DataRef const& /*ref*/) { + auto fillLabels = [digitSizes, rofSizes](TBranch& branch, std::vector const& labelbuffer, DataRef const& ref) { o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - LOG(info) << "WRITING " << labels.getNElements() << " LABELS "; + auto const* dh = DataRefUtils::getHeader(ref); + auto layer = static_cast(dh->subSpecification); + LOG(info) << "WRITING " << labels.getNElements() << " LABELS FOR " << layer << " WITH " << (*digitSizes)[layer] << " DIGITS IN " << (*rofSizes)[layer] << " ROFS"; o2::dataformats::IOMCTruthContainerView outputcontainer; auto ptr = &outputcontainer; @@ -83,35 +98,56 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::hea br->ResetAddress(); }; + auto getIndex = [](DataRef const& ref) -> size_t { + auto const* dh = DataRefUtils::getHeader(ref); + return static_cast(dh->subSpecification); + }; + auto getName = [](std::string base, size_t index) -> std::string { + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return base += "_" + std::to_string(index); + } + return base; + }; return MakeRootTreeWriterSpec((detStr + "DigitWriter" + (dec ? "_dec" : "")).c_str(), (detStrL + "digits.root").c_str(), - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Digits tree"}, + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = detStr + " Digits tree"}, MakeRootTreeWriterSpec::CustomClose(finishWriting), - // in case of labels we first read them as std::vector and process them correctly in the fillLabels hook - BranchDefinition>{InputSpec{(detStr + "_digitsMCTR").c_str(), detOrig, "DIGITSMCTR", 0}, - (detStr + "DigitMCTruth").c_str(), - (mctruth ? 1 : 0), fillLabels}, - BranchDefinition>{InputSpec{(detStr + "_digitsMC2ROF").c_str(), detOrig, "DIGITSMC2ROF", 0}, - (detStr + "DigitMC2ROF").c_str(), - (mctruth ? 1 : 0)}, - BranchDefinition>{InputSpec{(detStr + "digits").c_str(), detOrig, "DIGITS", 0}, - (detStr + "Digit").c_str(), - logger}, - BranchDefinition>{InputSpec{(detStr + "calib").c_str(), detOrig, "GBTCALIB", 0}, - (detStr + "Calib").c_str(), - (calib ? 1 : 0)}, - BranchDefinition>{InputSpec{(detStr + "digitsROF").c_str(), detOrig, "DIGITSROF", 0}, - (detStr + "DigitROF").c_str()})(); + BranchDefinition>{InputSpec{detStr + "digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}}, + detStr + "Digit", "digit-branch", + NLayers, + digitSizeGetter, + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "digitsROF", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}}, + detStr + "DigitROF", "digit-rof-branch", + NLayers, + rofSizeGetter, + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "_digitsMCTR", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}}, + detStr + "DigitMCTruth", "digit-mctruth-branch", + (mctruth ? NLayers : 0), + fillLabels, + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "_digitsMC2ROF", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}}, + detStr + "DigitMC2ROF", "digit-mc2rof-branch", + (mctruth ? NLayers : 0), + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "calib", ConcreteDataTypeMatcher{Origin, "GBTCALIB"}}, + detStr + "Calib", "digit-calib-branch", + (calib ? 1 : 0)})(); } DataProcessorSpec getITSDigitWriterSpec(bool mctruth, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib, o2::header::gDataOriginITS, o2::detectors::DetID::ITS); + return getDigitWriterSpec(mctruth, dec, calib); } DataProcessorSpec getMFTDigitWriterSpec(bool mctruth, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib, o2::header::gDataOriginMFT, o2::detectors::DetID::MFT); + return getDigitWriterSpec(mctruth, dec, calib); } } // end namespace itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx index 4edbc10d5bfbd..f90b708af1996 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx @@ -28,9 +28,8 @@ namespace o2 { namespace itsmft { - -EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits) - : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT), mGetDigits(getDigits) +EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, const std::string& ctfdictOpt) + : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT, ctfdictOpt), mGetDigits(getDigits) { assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mDetPrefix = orig == o2::header::gDataOriginITS ? "_ITS" : "_MFT"; @@ -119,7 +118,7 @@ void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matche } } -DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs; // this is a special dummy input which makes sense only in sync workflows @@ -141,20 +140,19 @@ DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosi inputs.emplace_back(std::string("ctf") + nm, orig, "CTFDATA", sspec, Lifetime::Timeframe); inputs.emplace_back(std::string("noise") + nm, orig, "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/NoiseMap", orig.as()))); inputs.emplace_back(std::string("cldict") + nm, orig, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", orig.as()))); - inputs.emplace_back(std::string("ctfdict") + nm, orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back(std::string("ctfdict") + nm, orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + } inputs.emplace_back(std::string("trigoffset"), "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ EntropyDecoderSpec::getName(orig), inputs, outputs, - AlgorithmSpec{adaptFromTask(orig, verbosity, getDigits)}, - Options{ - {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"mask-noise", VariantType::Bool, false, {"apply noise mask to digits or clusters (involves reclusterization)"}}, - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(orig, verbosity, getDigits, ctfdictOpt)}, + Options{{"mask-noise", VariantType::Bool, false, {"apply noise mask to digits or clusters (involves reclusterization)"}}, + {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx index 4b35f6cc44e39..a824184330547 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx @@ -27,9 +27,8 @@ namespace o2 { namespace itsmft { - -EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR) - : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) + : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT, ctfdictOpt), mSelIR(selIR) { assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mTimer.Stop(); @@ -112,7 +111,7 @@ void EntropyEncoderSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR) +DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("compClusters", orig, "COMPCLUSTERS", 0, Lifetime::Timeframe); @@ -123,19 +122,20 @@ DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR) inputs.emplace_back("cldict", orig, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", orig.as()))); inputs.emplace_back("alppar", orig, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", orig.as()))); } - inputs.emplace_back("ctfdict", orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + } return DataProcessorSpec{ orig == o2::header::gDataOriginITS ? "its-entropy-encoder" : "mft-entropy-encoder", inputs, Outputs{{orig, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, orig, "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(orig, 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(orig, 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 itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index 6b0585e293db6..5f09fd6c69a97 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -24,6 +24,7 @@ void customize(std::vector& workflowOptions) std::vector options{ ConfigParamSpec{"runmft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"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); @@ -40,7 +41,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT", selIR)); + wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT", selIR, cfgc.options().get("ctf-dict"))); } else { wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS", selIR)); } diff --git a/Detectors/MUON/MCH/Align/src/AlignRecordSpec.cxx b/Detectors/MUON/MCH/Align/src/AlignRecordSpec.cxx index 0a61d38a36b2a..690a17952b033 100644 --- a/Detectors/MUON/MCH/Align/src/AlignRecordSpec.cxx +++ b/Detectors/MUON/MCH/Align/src/AlignRecordSpec.cxx @@ -121,7 +121,7 @@ class AlignRecordTask mImproveCutChi2 = 2. * trackerParam.sigmaCutForImprovement * trackerParam.sigmaCutForImprovement; // Configuration for chamber fixing - auto input_fixchambers = ic.options().get("fix-chamber"); + auto input_fixchambers = ic.options().get("fix-chamber"); std::stringstream string_chambers(input_fixchambers); string_chambers >> std::ws; while (string_chambers.good()) { @@ -132,8 +132,8 @@ class AlignRecordTask } // Init for output saving - auto OutputRecFileName = ic.options().get("output-record-data"); - auto OutputConsFileName = ic.options().get("output-record-constraint"); + auto OutputRecFileName = ic.options().get("output-record-data"); + auto OutputConsFileName = ic.options().get("output-record-constraint"); mAlign.init(OutputRecFileName, OutputConsFileName); ic.services().get().set([this]() { diff --git a/Detectors/MUON/MCH/Align/src/AlignmentSpec.cxx b/Detectors/MUON/MCH/Align/src/AlignmentSpec.cxx index 9d92f18024d88..828cb0cb80242 100644 --- a/Detectors/MUON/MCH/Align/src/AlignmentSpec.cxx +++ b/Detectors/MUON/MCH/Align/src/AlignmentSpec.cxx @@ -167,7 +167,7 @@ class AlignmentTask LOG(info) << "Loading magnetic field and reference geometry from input files"; - auto grpFile = ic.options().get("grp-file"); + auto grpFile = ic.options().get("grp-file"); if (std::filesystem::exists(grpFile)) { const auto grp = parameters::GRPObject::loadFrom(grpFile); base::Propagator::initFieldFromGRP(grp); @@ -178,7 +178,7 @@ class AlignmentTask LOG(fatal) << "No GRP file"; } - IdealGeoFileName = ic.options().get("geo-file-ideal"); + IdealGeoFileName = ic.options().get("geo-file-ideal"); if (std::filesystem::exists(IdealGeoFileName)) { base::GeometryManager::loadGeometry(IdealGeoFileName.c_str()); transformation = geo::transformationFromTGeoManager(*gGeoManager); @@ -190,7 +190,7 @@ class AlignmentTask LOG(fatal) << "No ideal geometry"; } - RefGeoFileName = ic.options().get("geo-file-ref"); + RefGeoFileName = ic.options().get("geo-file-ref"); if (std::filesystem::exists(RefGeoFileName)) { base::GeometryManager::loadGeometry(RefGeoFileName.c_str()); transformation = geo::transformationFromTGeoManager(*gGeoManager); @@ -205,7 +205,7 @@ class AlignmentTask if (doReAlign) { LOG(info) << "Re-alignment mode"; LOG(info) << "Loading re-alignment geometry"; - NewGeoFileName = ic.options().get("geo-file-new"); + NewGeoFileName = ic.options().get("geo-file-new"); if (std::filesystem::exists(NewGeoFileName)) { base::GeometryManager::loadGeometry(NewGeoFileName.c_str()); transformation = geo::transformationFromTGeoManager(*gGeoManager); @@ -246,7 +246,7 @@ class AlignmentTask mImproveCutChi2 = 2. * trackerParam.sigmaCutForImprovement * trackerParam.sigmaCutForImprovement; // Fix chambers - TString chambersString = ic.options().get("fix-chamber"); + TString chambersString = ic.options().get("fix-chamber"); std::unique_ptr objArray(chambersString.Tokenize(",")); if (objArray->GetEntries() > 0) { for (int iVar = 0; iVar < objArray->GetEntries(); ++iVar) { @@ -256,8 +256,8 @@ class AlignmentTask } // Fix DEs - TString DEString = ic.options().get("fix-de"); - TString MaskDEString = ic.options().get("mask-fix-de"); + TString DEString = ic.options().get("fix-de"); + TString MaskDEString = ic.options().get("mask-fix-de"); std::unique_ptr objArrayDE(DEString.Tokenize(",")); std::unique_ptr objArrayMask(MaskDEString.Tokenize(",")); if (objArrayDE->GetEntries() > 0) { @@ -271,7 +271,7 @@ class AlignmentTask } doMatched = ic.options().get("matched"); - outFileName = ic.options().get("output"); + outFileName = ic.options().get("output"); readFromRec = ic.options().get("use-record"); if (readFromRec) { diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h index fc090c5c7e16d..5c9da95a98354 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h @@ -34,10 +34,10 @@ namespace o2 namespace mch { -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::MCH) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::MCH, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h index f28ca90e9a339..0c3534ff5cdd1 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h @@ -22,7 +22,7 @@ namespace o2 namespace mch { /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace mch } // namespace o2 diff --git a/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx b/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx index 9ec13fed85690..653120bd9b630 100644 --- a/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx +++ b/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace mch { - 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; @@ -43,7 +42,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, const char* specName, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"rofs"}, "MCH", "DIGITROFS", 0, Lifetime::Timeframe}, @@ -101,17 +100,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, uns std::vector inputs; inputs.emplace_back("ctf_MCH", "MCH", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_MCH", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_MCH", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ specName, 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 mch } // namespace o2 diff --git a/Detectors/MUON/MCH/Calibration/include/MCHCalibration/BadChannelCalibrator.h b/Detectors/MUON/MCH/Calibration/include/MCHCalibration/BadChannelCalibrator.h index 509b9b88b30e4..6873d340841a2 100644 --- a/Detectors/MUON/MCH/Calibration/include/MCHCalibration/BadChannelCalibrator.h +++ b/Detectors/MUON/MCH/Calibration/include/MCHCalibration/BadChannelCalibrator.h @@ -68,13 +68,17 @@ class BadChannelCalibrator final : public o2::calibration::TimeSlotCalibration(); mCalibrator->setSlotLength(o2::calibration::INFINITE_TF); mCalibrator->setUpdateAtTheEndOfRunOnly(); + mCalibrator->setLoggingInterval(mLoggingInterval); mTimeStamp = std::numeric_limits::max(); } @@ -52,6 +53,8 @@ void BadChannelCalibrationDevice::logStats(size_t dataSize) static auto loggerEnd = loggerStart; static size_t nDigits = 0; static size_t nTF = 0; + static size_t nTFtot = 0; + static size_t nTFtotWithData = 0; if (mLoggingInterval == 0) { return; @@ -59,11 +62,17 @@ void BadChannelCalibrationDevice::logStats(size_t dataSize) nDigits += dataSize; nTF += 1; + nTFtot += 1; + if (dataSize > 1000) { + nTFtotWithData += 1; + } loggerEnd = std::chrono::high_resolution_clock::now(); std::chrono::duration loggerElapsed = loggerEnd - loggerStart; - if (loggerElapsed.count() > 1000) { - LOG(info) << "received " << nDigits << " digits in " << nTF << " time frames"; + if (loggerElapsed.count() > mLoggingInterval) { + LOG(warning) << "received " << nDigits << " digits in " << nTF << " time frames"; + LOG(warning) << "received " << nTFtotWithData << " time frames with data out of " << nTFtot << " total time frames (" + << ((nTFtot > 0) ? (nTFtotWithData * 100.0) / nTFtot : 0.0) << "%)"; nDigits = 0; nTF = 0; loggerStart = std::chrono::high_resolution_clock::now(); @@ -86,7 +95,7 @@ void BadChannelCalibrationDevice::run(o2::framework::ProcessingContext& pc) std::string reason; if (mCalibrator->readyToSend(reason)) { mHasEnoughStat = true; - LOGP(info, "We're ready to send output to CCDB ({})", reason); + LOGP(warning, "We're ready to send output to CCDB ({})", reason); sendOutput(pc.outputs(), reason); mSkipData = true; } @@ -139,12 +148,12 @@ void sendCalibrationOutput(o2::framework::DataAllocator& output, using clbUtils = o2::calibration::Utils; auto image = o2::ccdb::CcdbApi::createObjectImage(payload, payloadInfo); - LOG(info) << "Sending object " << payloadInfo->getPath() - << " of type" << payloadInfo->getObjectType() - << " /" << payloadInfo->getFileName() - << " of size " << image->size() - << " bytes, valid for " << payloadInfo->getStartValidityTimestamp() - << " : " << payloadInfo->getEndValidityTimestamp(); + LOG(warning) << "Sending object " << payloadInfo->getPath() + << " of type" << payloadInfo->getObjectType() + << " /" << payloadInfo->getFileName() + << " of size " << image->size() + << " bytes, valid for " << payloadInfo->getStartValidityTimestamp() + << " : " << payloadInfo->getEndValidityTimestamp(); output.snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBPayload, "MCH_BADCHAN", subSpec}, *image.get()); output.snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBWrapper, "MCH_BADCHAN", subSpec}, *payloadInfo); @@ -168,7 +177,7 @@ void BadChannelCalibrationDevice::sendOutput(o2::framework::DataAllocator& outpu reason_with_entries = fmt::format("{} ; no entries", reason); } - LOGP(info, "sendOutput: {}", reason_with_entries); + LOGP(warning, "sendOutput: {}", reason_with_entries); mCalibrator->finalize(); // the bad channels table is only updated if there is enough statistics diff --git a/Detectors/MUON/MCH/Calibration/src/BadChannelCalibrator.cxx b/Detectors/MUON/MCH/Calibration/src/BadChannelCalibrator.cxx index 26d312e7dc36e..b5aa17ef81f8c 100644 --- a/Detectors/MUON/MCH/Calibration/src/BadChannelCalibrator.cxx +++ b/Detectors/MUON/MCH/Calibration/src/BadChannelCalibrator.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace o2::mch::calibration @@ -65,6 +66,8 @@ void BadChannelCalibrator::finalize() bool BadChannelCalibrator::hasEnoughData(const Slot& slot) const { + static auto loggerStart = std::chrono::high_resolution_clock::now(); + static auto loggerEnd = loggerStart; const int minNofEntries = BadChannelCalibratorParam::Instance().minRequiredNofEntriesPerChannel; const o2::mch::calibration::PedestalData* pedData = slot.getContainer(); auto nofChannels = pedData->size(); @@ -75,9 +78,35 @@ bool BadChannelCalibrator::hasEnoughData(const Slot& slot) const bool hasEnough = nofCalibrated > requiredChannels; - LOGP(info, - "nofChannelWithEnoughStat(>{})={} nofChannels={} requiredChannels={} hasEnough={}", - minNofEntries, nofCalibrated, nofChannels, requiredChannels, hasEnough); + // logging of calibration statistics + loggerEnd = std::chrono::high_resolution_clock::now(); + std::chrono::duration loggerElapsed = loggerEnd - loggerStart; + if (mLoggingInterval > 0 && loggerElapsed.count() > mLoggingInterval) { + int minEntriesPerChannel{std::numeric_limits::max()}; + int maxEntriesPerChannel{0}; + uint64_t averageEntriesPerChannel = 0; + std::for_each(pedData->cbegin(), pedData->cend(), + [&](const PedestalChannel& c) { + if (c.mEntries == 0) { + return; + } + if (c.mEntries > maxEntriesPerChannel) { + maxEntriesPerChannel = c.mEntries; + } + if (c.mEntries < minEntriesPerChannel) { + minEntriesPerChannel = c.mEntries; + } + averageEntriesPerChannel += c.mEntries; + }); + if (nofChannels > 0) { + averageEntriesPerChannel /= nofChannels; + } + LOGP(warning, "channel stats: min={} max={} average={}", minEntriesPerChannel, maxEntriesPerChannel, averageEntriesPerChannel); + LOGP(warning, + "nofChannelWithEnoughStat(>{})={} nofChannels={} requiredChannels={} hasEnough={}", + minNofEntries, nofCalibrated, nofChannels, requiredChannels, hasEnough); + loggerStart = std::chrono::high_resolution_clock::now(); + } return hasEnough; } @@ -92,7 +121,7 @@ void BadChannelCalibrator::finalizeSlot(Slot& slot) mBadChannelsVector.clear(); o2::mch::calibration::PedestalData* pedestalData = slot.getContainer(); - LOG(info) << "Finalize slot " << slot.getTFStart() << " <= TF <= " << slot.getTFEnd(); + LOG(warning) << "Finalize slot " << slot.getTFStart() << " <= TF <= " << slot.getTFEnd(); // keep track of first TimeFrame if (slot.getTFStart() < mTFStart) { @@ -120,9 +149,11 @@ void BadChannelCalibrator::finalizeSlot(Slot& slot) BadChannelCalibrator::Slot& BadChannelCalibrator::emplaceNewSlot(bool front, TFType tstart, TFType tend) { + const int nThreads = static_cast(BadChannelCalibratorParam::Instance().nThreads); auto& cont = getSlots(); auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); slot.setContainer(std::make_unique()); + slot.getContainer()->setNThreads(nThreads); return slot; } diff --git a/Detectors/MUON/MCH/Calibration/src/PedestalData.cxx b/Detectors/MUON/MCH/Calibration/src/PedestalData.cxx index 661bab7913b8e..5947cc940e3ce 100644 --- a/Detectors/MUON/MCH/Calibration/src/PedestalData.cxx +++ b/Detectors/MUON/MCH/Calibration/src/PedestalData.cxx @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include namespace o2::mch::calibration { @@ -69,45 +72,107 @@ PedestalData::PedestalMatrix PedestalData::initPedestalMatrix(uint16_t solarId) void PedestalData::fill(gsl::span digits) { bool mDebug = false; + static std::mutex pedestalMutex; + static std::set solarIds = o2::mch::raw::getSolarUIDs(); - for (auto& d : digits) { - uint16_t solarId = d.getSolarId(); - uint8_t dsId = d.getDsId(); - uint8_t channel = d.getChannel(); + if (digits.empty()) { + return; + } - auto iPedestal = mPedestals.find(solarId); + LOGP(info, "processing {} digits with {} threads", (int)digits.size(), mNThreads); - if (iPedestal == mPedestals.end()) { - auto iPedestalsNew = mPedestals.emplace(std::make_pair(solarId, initPedestalMatrix(solarId))); - iPedestal = iPedestalsNew.first; - } + // fill the queue of SOLAR IDs to be processed + std::queue solarQueue; + for (auto solarId : solarIds) { + solarQueue.push(solarId); + } - if (iPedestal == mPedestals.end()) { - LOGP(fatal, "failed to insert new element in padestals map"); - break; - } + auto processSolarDigits = [&]() { + while (true) { + int targetSolarId = -1; + PedestalsMap::iterator iPedestal; + bool pedestalsAreInitialized; + + // non thread-safe access to solarQueue, protected by the pedestalMutex + { + std::lock_guard lock(pedestalMutex); + + // stop when there are no mor SOLAR IDs to process + if (solarQueue.empty()) { + break; + } + + // get the next SOLAR ID to be processed + targetSolarId = solarQueue.front(); + solarQueue.pop(); + + // update the iterator to the pedestal data for the target SOLAR + iPedestal = mPedestals.find(targetSolarId); + if (iPedestal == mPedestals.end()) { + pedestalsAreInitialized = false; + } else { + pedestalsAreInitialized = true; + } + } - auto& ped = iPedestal->second[dsId][channel]; + // loop over digits, selecting only those belonging to the target SOLAR + for (auto& d : digits) { + uint16_t solarId = d.getSolarId(); + if (solarId != targetSolarId) { + continue; + } - for (uint16_t i = 0; i < d.nofSamples(); i++) { - auto s = d.getSample(i); + // non thread-safe access to Pedestals structure, protected by the pedestalMutex + if (!pedestalsAreInitialized) { + std::lock_guard lock(pedestalMutex); - ped.mEntries += 1; - uint64_t N = ped.mEntries; + // create the pedestals structure corresponding to the SOLAR ID to be processed + iPedestal = mPedestals.emplace(std::make_pair(targetSolarId, initPedestalMatrix(targetSolarId))).first; - double p0 = ped.mPedestal; - double p = p0 + (s - p0) / N; - ped.mPedestal = p; + if (iPedestal == mPedestals.end()) { + LOGP(fatal, "failed to insert new element in padestals map"); + break; + } + pedestalsAreInitialized = true; + } - double M0 = ped.mVariance; - double M = M0 + (s - p0) * (s - p); - ped.mVariance = M; - } + uint8_t dsId = d.getDsId(); + uint8_t channel = d.getChannel(); + + auto& ped = iPedestal->second[dsId][channel]; + + for (uint16_t i = 0; i < d.nofSamples(); i++) { + auto s = d.getSample(i); - if (mDebug) { - LOGP(info, "solarId {} dsId {} ch {} nsamples {} entries{} mean {} variance {}", - (int)solarId, (int)dsId, (int)channel, d.nofSamples(), ped.mEntries, ped.mPedestal, ped.mVariance); + ped.mEntries += 1; + uint64_t N = ped.mEntries; + + double p0 = ped.mPedestal; + double p = p0 + (s - p0) / N; + ped.mPedestal = p; + + double M0 = ped.mVariance; + double M = M0 + (s - p0) * (s - p); + ped.mVariance = M; + } + + if (mDebug) { + LOGP(info, "solarId {} dsId {} ch {} nsamples {} entries{} mean {} variance {}", + (int)solarId, (int)dsId, (int)channel, d.nofSamples(), ped.mEntries, ped.mPedestal, ped.mVariance); + } + } } + }; + + // process the digits in parallel threads + std::vector threads; + for (int ti = 0; ti < mNThreads; ti++) { + threads.emplace_back(processSolarDigits); + } + + // wait for all threads to finish processing + for (auto& thread : threads) { + thread.join(); } } diff --git a/Detectors/MUON/MCH/Calibration/test/testPedestalData.cxx b/Detectors/MUON/MCH/Calibration/test/testPedestalData.cxx index c61656aa7845f..0c1d6bffb984e 100644 --- a/Detectors/MUON/MCH/Calibration/test/testPedestalData.cxx +++ b/Detectors/MUON/MCH/Calibration/test/testPedestalData.cxx @@ -84,6 +84,17 @@ BOOST_AUTO_TEST_CASE(TestIteratorOnCompletePedestalData) ++n; } BOOST_TEST(n == allDigits.size()); + + // multi-threaded version + PedestalData pdmt; + pdmt.setNThreads(8); + pdmt.fill(allDigits); + + int nmt{0}; + for (const auto& ped : pdmt) { + ++nmt; + } + BOOST_TEST(nmt == allDigits.size()); } BOOST_AUTO_TEST_CASE(TestIteratorEquality) @@ -113,6 +124,16 @@ BOOST_AUTO_TEST_CASE(TestIteratorPreIncrementable) n++; } BOOST_TEST(n == 2768); + + // multi-threaded version + PedestalData pdmt; + pdmt.setNThreads(8); + pdmt.fill(digits); + int nmt{0}; + for (auto rec : pdmt) { + nmt++; + } + BOOST_TEST(nmt == 2768); // 2768 = 1856 pads in solar 328 + 721 pads in solar 721 // Note that solar 328 has 29 dual sampas // solar 721 has 15 dual sampas diff --git a/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx b/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx index 685971a026b27..d5bf4cad2142d 100644 --- a/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx +++ b/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx @@ -166,7 +166,7 @@ void zeroMisAlignGeometry(const std::string& ccdbHost, const std::string& fileNa std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detMCH) : 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/MUON/MCH/Geometry/Test/misAlign.C b/Detectors/MUON/MCH/Geometry/Test/misAlign.C index c49fe5717a36b..41312b1f223d4 100644 --- a/Detectors/MUON/MCH/Geometry/Test/misAlign.C +++ b/Detectors/MUON/MCH/Geometry/Test/misAlign.C @@ -89,7 +89,7 @@ void misAlign(Double_t xcartmisaligm = 0.01, Double_t xcartmisaligw = 0.0, std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detMCH) : 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/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx b/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx deleted file mode 100644 index b614346f1a42a..0000000000000 --- a/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @author Laurent Aphecetche - -#include "MCHMappingSegContour/CathodeSegmentationSVGWriter.h" -#include "MCHMappingInterface/CathodeSegmentation.h" -#include "MCHMappingSegContour/CathodeSegmentationContours.h" -#include "MCHContour/SVGWriter.h" -#include - -using namespace o2::mch::contour; - -namespace o2 -{ -namespace mch -{ -namespace mapping -{ - -std::string svgCathodeSegmentationDefaultStyle() -{ - return R"( -.pads { - fill: #EEEEEE; - stroke-width: 0.025px; - stroke: #AAAAAA; -} -.padchannels { - font-size: 0.4px; - font-family: arial; - fill: blue; - text-anchor: middle; -} -.dualsampas { - fill:none; - stroke-width: 0.025px; - stroke: #333333; -} -.detectionelements { - fill:none; - stroke-width:0.025px; - stroke: #000000; -} -.testpoints { - fill:red; - stroke-width:0.025px; - stroke: black; - opacity: 0.5; -} -)"; -} - -void svgCathodeSegmentation(const CathodeSegmentation& seg, SVGWriter& w, bool showdes, bool showdualsampas, bool showpads, - bool showpadchannels) -{ - std::vector> dualSampaContours = getDualSampaContours(seg); - std::vector>> dualSampaPads = getPadPolygons(seg); - std::vector> dualSampaPadChannels = getPadChannels(seg); - - if (dualSampaPadChannels.size() != dualSampaPads.size()) { - throw std::runtime_error("gouze"); - } - - auto deContour = getEnvelop(seg); - auto box = getBBox(seg); - - if (showpads) { - w.svgGroupStart("pads"); - for (auto& dsp : dualSampaPads) { - for (auto& p : dsp) { - w.polygon(p); - } - } - w.svgGroupEnd(); - } - - if (showpadchannels) { - w.svgGroupStart("padchannels"); - for (auto i = 0; i < dualSampaPads.size(); ++i) { - auto& dsp = dualSampaPads[i]; - auto& dspch = dualSampaPadChannels[i]; - for (auto j = 0; j < dsp.size(); j++) { - auto bbox = getBBox(dsp[j]); - w.text(std::to_string(dspch[j]), bbox.xcenter(), - bbox.ymax() - 0.05 * bbox.height()); // SVG text y position is the bottom of the text - } - } - w.svgGroupEnd(); - } - - if (showdualsampas) { - w.svgGroupStart("dualsampas"); - for (auto& dsp : dualSampaContours) { - w.contour(dsp); - } - w.svgGroupEnd(); - } - - if (showdes) { - w.svgGroupStart("detectionelements"); - w.contour(deContour); - } -} - -} // namespace mapping -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx deleted file mode 100644 index 52e4581da1a71..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx +++ /dev/null @@ -1,938 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_CRU_BARE_CHARGESUM; -template <> -gsl::span REF_BUFFER_CRU() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_CRU_BARE_CHARGESUM[0]), REF_BUFFER_CRU_BARE_CHARGESUM.size()); -} -std::array REF_BUFFER_CRU_BARE_CHARGESUM = { - // clang-format off -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xB0, 0x12, 0xB0, 0x12, -0x00, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0D, 0x10, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x40, 0x0A, 0x40, 0x0A, -0x00, 0x00, 0x0F, 0x00, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1E, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0F, 0x00, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x10, 0x0D, 0x10, 0x0D, -0x07, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAB, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAB, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x07, 0x01, 0x0D, 0x10, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx deleted file mode 100644 index 3c3781460f4d1..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx +++ /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. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_CRU_USERLOGIC_CHARGESUM; -template <> -gsl::span REF_BUFFER_CRU() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_CRU_USERLOGIC_CHARGESUM[0]), REF_BUFFER_CRU_USERLOGIC_CHARGESUM.size()); -} -std::array REF_BUFFER_CRU_USERLOGIC_CHARGESUM = { - // clang-format off -0x04, 0x40, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, -0x0F, 0x00, 0x0F, 0x00, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0xA1, 0x00, -0x03, 0x12, 0x00, 0xE3, 0x46, 0x00, 0xA0, 0x00, 0x01, 0x60, 0xD0, 0x00, -0x00, 0x58, 0xA2, 0x00, 0x04, 0x40, 0xBB, 0x11, 0x00, 0x01, 0xA0, 0x00, -0x18, 0x14, 0x02, 0x40, 0x90, 0x04, 0xA0, 0x00, 0x70, 0x6F, 0x04, 0x40, -0x00, 0x18, 0xA0, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, -0xED, 0xDE, 0xED, 0xFE, 0xED, 0xDE, 0xED, 0xFE, 0x04, 0x40, 0x00, 0x00, -0x1E, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x0F, 0x01, 0x0F, 0x00, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, -0x0F, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x81, 0x38, -0x1A, 0x12, 0x80, 0xE0, 0x46, 0x00, 0x80, 0x38, 0x01, 0x60, 0xA0, 0x00, -0x00, 0x4D, 0x82, 0x38, 0x04, 0x60, 0xB8, 0x11, 0x00, 0x01, 0x80, 0x38, -0x18, 0x50, 0x00, 0x80, 0x90, 0x04, 0x80, 0x38, 0x28, 0x6E, 0x04, 0x40, -0x00, 0x18, 0x80, 0x38, 0x1E, 0x00, 0x50, 0x21, 0x01, 0x38, 0x82, 0x38, -0x1B, 0x01, 0x10, 0x00, 0x06, 0x28, 0x80, 0x38, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x80, 0x38, 0xED, 0xDE, 0xED, 0xFE, 0xED, 0xDE, 0xED, 0xFE, -0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x01, 0x04, 0x03, 0x12, 0x40, 0xF6, -0x46, 0x00, 0x00, 0x04, 0x01, 0x60, 0x40, 0x1A, 0x00, 0x54, 0x02, 0x04, -0x04, 0xD0, 0xBD, 0x11, 0x00, 0x01, 0x00, 0x04, 0x18, 0xB8, 0x06, 0x00, -0x96, 0x04, 0x00, 0x04, 0x84, 0x6F, 0x04, 0x40, 0x00, 0x18, 0x00, 0x04, -0xB8, 0x01, 0xF0, 0x20, 0x01, 0x94, 0x03, 0x04, 0x1B, 0x01, 0x10, 0x00, -0x06, 0xC2, 0x01, 0x04, 0x00, 0x00, 0x48, 0x00, 0xE9, 0x1B, 0x01, 0x04, -0x00, 0x04, 0x80, 0x01, 0x73, 0x00, 0x00, 0x04, 0x48, 0x12, 0x50, 0xEA, -0x46, 0x00, 0x00, 0x04, 0x01, 0x60, 0x40, 0x1A, 0x00, 0x00, 0x00, 0x04, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, -0x0F, 0x01, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx deleted file mode 100644 index 89b1602cb0489..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_GBT_BARE_CHARGESUM; -template <> -gsl::span REF_BUFFER_GBT() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_GBT_BARE_CHARGESUM[0]), REF_BUFFER_GBT_BARE_CHARGESUM.size()); -} -std::array REF_BUFFER_GBT_BARE_CHARGESUM = { - // clang-format off -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xEB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xBD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xEB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xE8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xE8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx deleted file mode 100644 index 9487037328ad2..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_GBT_USERLOGIC_CHARGESUM; -template <> -gsl::span REF_BUFFER_GBT() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_GBT_USERLOGIC_CHARGESUM[0]), REF_BUFFER_GBT_USERLOGIC_CHARGESUM.size()); -} -std::array REF_BUFFER_GBT_USERLOGIC_CHARGESUM = { - // clang-format off -0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x01, 0x58, 0x0C, 0x12, 0x00, 0xA0, -0x50, 0x03, 0x00, 0x58, 0x01, 0x30, 0xA0, 0x00, 0x00, 0x5B, 0x02, 0x58, -0x04, 0xC0, 0x2F, 0xD4, 0x00, 0x01, 0x00, 0x58, 0x0C, 0x80, 0x02, 0x00, -0x00, 0x00, 0x00, 0x58, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x61, 0x58, -0x19, 0x12, 0x60, 0xAD, 0x50, 0x03, 0x60, 0x58, 0x01, 0x30, 0xD0, 0x00, -0x00, 0x09, 0x62, 0x58, 0x04, 0x5C, 0x28, 0xD4, 0x00, 0x01, 0x60, 0x58, -0x0C, 0x14, 0x02, 0x40, 0x82, 0x04, 0x60, 0x58, 0xF7, 0x0B, 0x35, 0x40, -0x00, 0x0C, 0x60, 0x58, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x58 - - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx index 058202dfb802b..b5f371edfc759 100644 --- a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace mch { - 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; @@ -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(); @@ -85,12 +84,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(const char* specName, bool selIR) +DataProcessorSpec getEntropyEncoderSpec(const char* specName, bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("rofs", "MCH", "DIGITROFS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "MCH", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -99,14 +101,12 @@ DataProcessorSpec getEntropyEncoderSpec(const char* specName, bool selIR) inputs, Outputs{{"MCH", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "MCH", "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 mch } // namespace o2 @@ -118,6 +118,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, "ccdb", {"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); @@ -133,6 +134,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); - wf.emplace_back(o2::mch::getEntropyEncoderSpec("mch-entropy-encoder", selIR)); + wf.emplace_back(o2::mch::getEntropyEncoderSpec("mch-entropy-encoder", selIR, cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h index 3071b65db47b1..defec7207f808 100644 --- a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -34,10 +34,10 @@ namespace o2 namespace mid { -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::MID) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::MID, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C index 5cec2c611bcf8..06aca991be338 100644 --- a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C +++ b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C @@ -28,7 +28,6 @@ #include "TGraph.h" #include "TTimeStamp.h" #include "CCDB/CcdbApi.h" -#include "DataFormatsParameters/GRPECSObject.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsMID/ColumnData.h" #include "MIDBase/ColumnDataHandler.h" @@ -111,12 +110,18 @@ std::string timeRangeToString(long start, long end) std::vector findObjectsMDInPeriod(long start, long end, const o2::ccdb::CcdbApi& api, const char* path) { std::vector mds; - auto out = api.list(path, false, "application/json", getTSMS(end), getTSMS(start)); + long creationDelayMS = 300000; // The objects can be created up to 5 minutes after the end of run + auto out = api.list(path, false, "application/json", getTSMS(end) + creationDelayMS, getTSMS(start)); rapidjson::Document doc; doc.Parse(out.c_str()); for (auto& obj : doc["objects"].GetArray()) { MDStruct md; md.start = obj["validFrom"].GetInt64(); + if (getTSMS(end) < getTSMS(md.start)) { + // Since we query on the creation time, adding a delay + // we need to cross-check here that we are within the run + continue; + } md.end = obj["validUntil"].GetInt64(); md.runNumber = std::atoi(obj["RunNumber"].GetString()); md.runType = obj["RunType"].GetString(); diff --git a/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx b/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx deleted file mode 100644 index a54ea9c1733a8..0000000000000 --- a/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MID/Tracking/test/bench_Tracker.cxx -/// \brief Benchmark tracker device for MID -/// \author Diego Stocco -/// \date 17 March 2018 - -#include "benchmark/benchmark.h" -#include -#include "DataFormatsMID/Cluster.h" -#include "DataFormatsMID/Track.h" -#include "MIDBase/HitFinder.h" -#include "MIDBase/Mapping.h" -#include "MIDBase/MpArea.h" -#include "MIDTestingSimTools/TrackGenerator.h" -#include "MIDTracking/Tracker.h" - -std::vector generateTestData(int nTracks, o2::mid::TrackGenerator& trackGen, - const o2::mid::HitFinder& hitFinder, const o2::mid::Mapping& mapping) -{ - o2::mid::Mapping::MpStripIndex stripIndex; - o2::mid::MpArea area; - std::vector clusters; - o2::mid::Cluster cl; - std::vector tracks = trackGen.generate(nTracks); - for (auto& track : tracks) { - for (int ich = 0; ich < 4; ++ich) { - auto hits = hitFinder.getLocalPositions(track, ich); - bool isFired = false; - for (auto& hit : hits) { - int deId = hit.deId; - float xPos = hit.xCoor; - float yPos = hit.yCoor; - stripIndex = mapping.stripByPosition(xPos, yPos, 0, deId, false); - if (!stripIndex.isValid()) { - continue; - } - cl.deId = deId; - area = mapping.stripByLocation(stripIndex.strip, 0, stripIndex.line, stripIndex.column, deId); - cl.yCoor = area.getCenterY(); - cl.yErr = area.getHalfSizeY() / std::sqrt(3.); - stripIndex = mapping.stripByPosition(xPos, yPos, 1, deId, false); - area = mapping.stripByLocation(stripIndex.strip, 1, stripIndex.line, stripIndex.column, deId); - cl.xCoor = area.getCenterX(); - cl.xErr = area.getHalfSizeX() / std::sqrt(3.); - clusters.push_back(cl); - } // loop on fired pos - } // loop on chambers - } // loop on tracks - return clusters; -} - -static void BM_TRACKER(benchmark::State& state) -{ - o2::mid::GeometryTransformer geoTrans = o2::mid::createDefaultTransformer(); - o2::mid::TrackGenerator trackGen; - o2::mid::HitFinder hitFinder(geoTrans); - o2::mid::Mapping mapping; - o2::mid::Tracker tracker(geoTrans); - - int nTracksPerEvent = state.range(0); - tracker.init((state.range(1) == 1)); - double num{0}; - - std::vector inputData; - - for (auto _ : state) { - state.PauseTiming(); - inputData = generateTestData(nTracksPerEvent, trackGen, hitFinder, mapping); - state.ResumeTiming(); - tracker.process(inputData); - ++num; - } - - state.counters["num"] = benchmark::Counter(num, benchmark::Counter::kIsRate); -} - -static void CustomArguments(benchmark::internal::Benchmark* bench) -{ - for (int itrack = 1; itrack <= 8; ++itrack) { - for (int imethod = 0; imethod < 2; ++imethod) { - bench->Args({itrack, imethod}); - } - } -} - -BENCHMARK(BM_TRACKER)->Apply(CustomArguments)->Unit(benchmark::kNanosecond); - -BENCHMARK_MAIN(); diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h index 301db519b9a5f..8f466ac8b7a54 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace mid 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 mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h index e90c96e6ac8fe..20858ca6dfc07 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace mid 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; @@ -43,7 +43,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 mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h deleted file mode 100644 index b5a6b33530c8f..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MIDWorkflow/RawAggregatorSpec.h -/// \brief Data processor spec for MID raw data aggregator devices -/// \author Diego Stocco -/// \date 26 February 2020 - -#ifndef O2_MID_RAWAGGREGATORSPEC_H -#define O2_MID_RAWAGGREGATORSPEC_H - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::DataProcessorSpec getRawAggregatorSpec(); -} // namespace mid -} // namespace o2 - -#endif //O2_MID_RAWAGGREGATORSPEC_H diff --git a/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx b/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx deleted file mode 100644 index 77d05a8b3374f..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MID/Workflow/src/RawDumpSpec.cxx -/// \brief Device to dump decoded raw data -/// \author Diego Stocco -/// \date 17 February 2022 - -#include "MIDWorkflow/RawDumpSpec.h" - -#include -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Logger.h" -#include "Framework/Task.h" -#include "fmt/format.h" -#include "DataFormatsMID/ROBoard.h" -#include "DataFormatsMID/ROFRecord.h" - -namespace o2 -{ -namespace mid -{ - -class RawDumpDeviceDPL -{ - public: - void init(o2::framework::InitContext& ic) - { - auto outFilename = ic.options().get("mid-dump-outfile"); - - if (!outFilename.empty()) { - mOutFile.open(outFilename.c_str()); - } - } - - void - run(o2::framework::ProcessingContext& pc) - { - - auto data = pc.inputs().get>("mid_decoded"); - auto dataROFs = pc.inputs().get>("mid_decoded_rof"); - std::stringstream ss; - for (auto& rof : dataROFs) { - ss << fmt::format("BCid: 0x{:x} Orbit: 0x{:x} EvtType: {:d}", rof.interactionRecord.bc, rof.interactionRecord.orbit, static_cast(rof.eventType)) << std::endl; - for (auto colIt = data.begin() + rof.firstEntry, end = data.begin() + rof.getEndIndex(); colIt != end; ++colIt) { - ss << *colIt << std::endl; - } - } - if (mOutFile.is_open()) { - mOutFile << ss.str(); - } else { - LOG(info) << ss.str(); - } - } - - private: - std::ofstream mOutFile; /// Output file -}; - -framework::DataProcessorSpec getRawDumpSpec() -{ - std::vector inputSpecs{ - o2::framework::InputSpec{"mid_decoded", header::gDataOriginMID, "DECODED", 0, o2::framework::Lifetime::Timeframe}, - o2::framework::InputSpec{"mid_decoded_rof", header::gDataOriginMID, "DECODEDROF", 0, o2::framework::Lifetime::Timeframe}}; - - return o2::framework::DataProcessorSpec{ - "MIDRawDataDumper", - {inputSpecs}, - {}, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask()}, - o2::framework::Options{{"mid-dump-outfile", o2::framework::VariantType::String, "", {"Dump output to file"}}}}; -} - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx index 5a8df6f8e81cb..0f6dc8bbaa995 100644 --- a/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx @@ -26,8 +26,7 @@ namespace o2 { namespace mid { - -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(); @@ -84,7 +83,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; for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { @@ -94,17 +93,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) outputs.emplace_back(OutputSpec{{"ctfrep"}, "MID", "CTFDECREP", 0, Lifetime::Timeframe}); std::vector inputs; inputs.emplace_back("ctf_MID", "MID", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_MID", "MID", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_MID", "MID", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "mid-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 mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx index a472d6e28ff16..d75fe3fa6fbf2 100644 --- a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx @@ -32,8 +32,7 @@ namespace o2 { namespace mid { - -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(); @@ -100,12 +99,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("rofs", ConcreteDataTypeMatcher(header::gDataOriginMID, "DATAROF"), Lifetime::Timeframe); inputs.emplace_back("cols", ConcreteDataTypeMatcher(header::gDataOriginMID, "DATA"), Lifetime::Timeframe); - inputs.emplace_back("ctfdict", header::gDataOriginMID, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", header::gDataOriginMID, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -114,13 +116,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{header::gDataOriginMID, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, header::gDataOriginMID, "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 mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx b/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx deleted file mode 100644 index 036b63bc75338..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MID/Workflow/src/raw-dump-workflow.cxx -/// \brief MID raw dump workflow -/// \author Diego Stocco -/// \date 17 February 2022 - -#include -#include -#include "Framework/Variant.h" -#include "Framework/ConfigParamSpec.h" -#include "MIDRaw/CrateMasks.h" -#include "MIDRaw/ElectronicsDelay.h" -#include "MIDRaw/FEEIdConfig.h" -#include "MIDWorkflow/RawDumpSpec.h" -#include "MIDWorkflow/RawDecoderSpec.h" - -using namespace o2::framework; - -// add workflow options, note that customization needs to be declared before -// including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - std::vector - options{ - {"feeId-config-file", VariantType::String, "", {"Filename with crate FEE ID correspondence"}}, - {"crate-masks-file", VariantType::String, "", {"Filename with crate masks"}}, - {"electronics-delay-file", VariantType::String, "", {"Filename with electronics delay"}}}; - workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); -} - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - auto feeIdConfigFilename = cfgc.options().get("feeId-config-file"); - o2::mid::FEEIdConfig feeIdConfig; - if (!feeIdConfigFilename.empty()) { - feeIdConfig = o2::mid::FEEIdConfig(feeIdConfigFilename.c_str()); - } - auto crateMasksFilename = cfgc.options().get("crate-masks-file"); - o2::mid::CrateMasks crateMasks; - if (!crateMasksFilename.empty()) { - crateMasks = o2::mid::CrateMasks(crateMasksFilename.c_str()); - } - auto electronicsDelayFilename = cfgc.options().get("electronics-delay-file"); - o2::mid::ElectronicsDelay electronicsDelay; - if (!electronicsDelayFilename.empty()) { - electronicsDelay = o2::mid::readElectronicsDelay(electronicsDelayFilename.c_str()); - } - - WorkflowSpec specs; - specs.emplace_back(o2::mid::getRawDecoderSpec(true, feeIdConfig, crateMasks, electronicsDelay, false)); - specs.emplace_back(o2::mid::getRawDumpSpec()); - return specs; -} diff --git a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx index 56c482c514e38..25b038190281a 100644 --- a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MID/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, "ccdb", {"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::mid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::mid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h b/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h index 4457da2e100ad..0814fe0da4547 100644 --- a/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h +++ b/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h @@ -76,7 +76,7 @@ class TurnOnHistos /// \param bitset with channels fired in event void fillFiredMap(const std::bitset& bs) { - for (short i = NCHANNELS; --i;) { + for (size_t i = 0; i < NCHANNELS; ++i) { if (bs[i]) { mGoodMap[i]++; } @@ -87,7 +87,7 @@ class TurnOnHistos /// \param bitset with channels fired in event void fillNoisyMap(const std::bitset& bs) { - for (short i = NCHANNELS; --i;) { + for (size_t i = 0; i < NCHANNELS; ++i) { if (bs[i]) { mNoisyMap[i]++; } diff --git a/Detectors/PHOS/calib/src/PHOSRunbyrunCalibrator.cxx b/Detectors/PHOS/calib/src/PHOSRunbyrunCalibrator.cxx index baa20307b0fbd..63e51f06c0e64 100644 --- a/Detectors/PHOS/calib/src/PHOSRunbyrunCalibrator.cxx +++ b/Detectors/PHOS/calib/src/PHOSRunbyrunCalibrator.cxx @@ -127,11 +127,11 @@ bool PHOSRunbyrunSlot::checkCluster(const Cluster& clu) return false; } // First check BadMap - float posX, posZ; + float posX{0}, posZ{0}; clu.getLocalPosition(posX, posZ); - short absId; + short absId{0}; Geometry::relPosToAbsId(clu.module(), posX, posZ, absId); - if (!mBadMap->isChannelGood(absId)) { + if (mBadMap && absId >= 0 && !mBadMap->isChannelGood(absId)) { return false; } return (clu.getEnergy() > 0.3 && clu.getMultiplicity() > 1); diff --git a/Detectors/PHOS/calib/src/PHOSTurnonCalibrator.cxx b/Detectors/PHOS/calib/src/PHOSTurnonCalibrator.cxx index 5413b20f491b8..432090c280ff8 100644 --- a/Detectors/PHOS/calib/src/PHOSTurnonCalibrator.cxx +++ b/Detectors/PHOS/calib/src/PHOSTurnonCalibrator.cxx @@ -36,7 +36,7 @@ PHOSTurnonSlot::PHOSTurnonSlot(bool useCCDB) : mUseCCDB(useCCDB) PHOSTurnonSlot::PHOSTurnonSlot(const PHOSTurnonSlot& other) { mUseCCDB = other.mUseCCDB; - mRunStartTime = other.mUseCCDB; + mRunStartTime = other.mRunStartTime; mFiredTiles.reset(); mNoisyTiles.reset(); mTurnOnHistos = std::make_unique(); @@ -91,15 +91,17 @@ void PHOSTurnonSlot::scanClusters(const gsl::span& cells, const Trig for (int i = firstCellInEvent; i < lastCellInEvent; i++) { const Cell& c = cells[i]; if (c.getTRU()) { - mNoisyTiles.set(c.getTRUId() - Geometry::getTotalNCells() - 1); + auto channel = c.getTRUId() - Geometry::getTotalNCells() - 1; + if (channel >= 0) { + mNoisyTiles.set(channel); + } } } // Copy to have good and noisy map mFiredTiles.reset(); - char mod; - float x, z; - short ddl; + float x{0}, z{0}; + short ddl{0}; int firstCluInEvent = clutr.getFirstEntry(); int lastCluInEvent = firstCluInEvent + clutr.getNumberOfObjects(); for (int i = firstCluInEvent; i < lastCluInEvent; i++) { @@ -107,7 +109,7 @@ void PHOSTurnonSlot::scanClusters(const gsl::span& cells, const Trig if (clu.getEnergy() < 1.e-4) { continue; } - mod = clu.module(); + char mod = clu.module(); clu.getLocalPosition(x, z); // TODO: do we need separate 2x2 and 4x4 spectra? Switch? // short truId2x2 = Geometry::relPosToTruId(mod, x, z, 0); @@ -123,7 +125,7 @@ void PHOSTurnonSlot::scanClusters(const gsl::span& cells, const Trig // Fill final good and noisy maps mTurnOnHistos->fillFiredMap(mFiredTiles); mNoisyTiles ^= mFiredTiles; - mTurnOnHistos->fillNoisyMap(mFiredTiles); + mTurnOnHistos->fillNoisyMap(mNoisyTiles); } //============================================== diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h index 96ee5093bacca..e222328a351c0 100644 --- a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -32,10 +32,10 @@ namespace o2 namespace phos { -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::PHS) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::PHS, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx b/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx index 37ba3ecae7159..aa929f8d6c6e5 100644 --- a/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx +++ b/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx @@ -13,6 +13,7 @@ /// \author Dmitri Peresunko #include +#include #include "PHOSReconstruction/CaloRawFitter.h" #include "PHOSBase/PHOSSimParams.h" diff --git a/Detectors/PHOS/reconstruction/src/CaloRawFitterGS.cxx b/Detectors/PHOS/reconstruction/src/CaloRawFitterGS.cxx index 08c1f7fb14bc1..3ea506cf203aa 100644 --- a/Detectors/PHOS/reconstruction/src/CaloRawFitterGS.cxx +++ b/Detectors/PHOS/reconstruction/src/CaloRawFitterGS.cxx @@ -13,6 +13,8 @@ /// \author Dmitri Peresunko #include +#include +#include #include "PHOSReconstruction/CaloRawFitterGS.h" #include "PHOSBase/PHOSSimParams.h" diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h index 1890864af77ea..a6045cf36f7b2 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace phos 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 phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h index 4ac8240f4c234..c88bddedc7e20 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace phos 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 phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx b/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx index a3d15862a2057..20b161b2d2325 100644 --- a/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace phos { - -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 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "PHS", "CELLTRIGREC", 0, Lifetime::Timeframe}, @@ -83,17 +82,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_PHS", "PHS", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_PHS", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_PHS", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "phos-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 phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx b/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx index a932a45f1bb53..66a0e04ed3895 100644 --- a/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace phos { - -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("triggers", "PHS", "CELLTRIGREC", 0, Lifetime::Timeframe); inputs.emplace_back("cells", "PHS", "CELLS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -84,13 +86,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"PHS", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "PHS", "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 phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx b/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx index 454be7a5fcb83..06baf889b662f 100644 --- a/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx +++ b/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx @@ -19,7 +19,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "MathUtils/Utils.h" using namespace o2::framework; @@ -106,7 +105,7 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) o2::math_utils::detail::truncateFloatFraction(c.getTime(), mCaloTime), c.getType(), // HG/LG 0); // hard coded for phos (-1 would be undefined, 0 phos) - } // end of cell loop + } // end of cell loop auto bcID = tr.getBCData().toLong(); bcCursor(0, diff --git a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx index c7266925060c2..41642cd026089 100644 --- a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/PHOS/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, "ccdb", {"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::phos::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::phos::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/Passive/CMakeLists.txt b/Detectors/Passive/CMakeLists.txt index 0976530bc6571..a24954ad10539 100644 --- a/Detectors/Passive/CMakeLists.txt +++ b/Detectors/Passive/CMakeLists.txt @@ -23,6 +23,7 @@ o2_add_library(DetectorsPassive src/Hall.cxx src/HallSimParam.cxx src/PassiveBase.cxx + src/ExternalModule.cxx PUBLIC_LINK_LIBRARIES O2::Field O2::DetectorsBase O2::SimConfig) o2_target_root_dictionary(DetectorsPassive @@ -39,6 +40,7 @@ o2_target_root_dictionary(DetectorsPassive include/DetectorsPassive/Hall.h include/DetectorsPassive/HallSimParam.h include/DetectorsPassive/PassiveBase.h + include/DetectorsPassive/ExternalModule.h LINKDEF src/PassiveLinkDef.h) # FIXME: if PutFrameInTop really depends on TRD, then the following can not work diff --git a/Detectors/Passive/include/DetectorsPassive/ExternalModule.h b/Detectors/Passive/include/DetectorsPassive/ExternalModule.h new file mode 100644 index 0000000000000..155870ae42a6d --- /dev/null +++ b/Detectors/Passive/include/DetectorsPassive/ExternalModule.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_PASSIVE_EXTERNALMODULE_H +#define ALICEO2_PASSIVE_EXTERNALMODULE_H + +#include "DetectorsPassive/PassiveBase.h" // base class of passive modules +#include "Rtypes.h" // for Pipe::Class, ClassDef, Pipe::Streamer + +class TGeoVolume; +class TGeoTransformation; + +namespace o2 +{ +namespace passive +{ + +// options used to configure a generic plug and play external module +struct ExternalModuleOptions { + std::string root_macro_file; // the file where to lookup the ROOT geometry building macro + std::string top_volume; // the volume to be added + std::string anchor_volume; // the volume into which the detector will be hooked + TGeoMatrix const* placement = nullptr; // how to place the module inside anchor_volume +}; + +// a module (passive material) defined externally (ROOT macro / GDML / TGeo geometry) +class ExternalModule : public PassiveBase +{ + public: + ExternalModule(const char* name, const char* long_title, ExternalModuleOptions options); + ExternalModule() = default; // default constructor + + ~ExternalModule() override = default; + void ConstructGeometry() override; + + /// Clone this object (used in MT mode only) + FairModule* CloneModule() const override { return nullptr; } + + typedef std::function GeomBuilderFcn; // function hook for external geometry builder + + private: + // void createMaterials(); + ExternalModule(const ExternalModule& orig); + ExternalModule& operator=(const ExternalModule&); + + GeomBuilderFcn mGeomHook; + ExternalModuleOptions mOptions; + + bool initGeomBuilderHook(); // function to load/JIT Geometry builder hook + void remapMedia(TGeoVolume* vol); // performs a remapping of materials/media IDs after registration with VMC + + // ClassDefOverride(ExternalModule, 0); +}; +} // namespace passive +} // namespace o2 +#endif diff --git a/Detectors/Passive/include/DetectorsPassive/PipeRun4.h b/Detectors/Passive/include/DetectorsPassive/PipeRun4.h index 1943bb25a802f..5eadb7af1003c 100644 --- a/Detectors/Passive/include/DetectorsPassive/PipeRun4.h +++ b/Detectors/Passive/include/DetectorsPassive/PipeRun4.h @@ -46,7 +46,7 @@ class PipeRun4 : public PassiveBase TGeoPcon* makeMotherFromTemplate(const TGeoPcon* shape, int imin = -1, int imax = -1, float r0 = 0., int nz = -1); TGeoPcon* makeInsulationFromTemplate(TGeoPcon* shape); - TGeoVolume* makeBellow(const char* ext, int nc, float rMin, float rMax, float dU, float rPlie, + TGeoVolume* makeBellow(const char* ext, int nc, float rMin, float rMax, float rPlie, float dPlie); TGeoVolume* makeBellowCside(const char* ext, int nc, float rMin, float rMax, float rPlie, float dPlie); diff --git a/Detectors/Passive/src/Cave.cxx b/Detectors/Passive/src/Cave.cxx index e2ea513095c72..208084a335ab5 100644 --- a/Detectors/Passive/src/Cave.cxx +++ b/Detectors/Passive/src/Cave.cxx @@ -85,23 +85,27 @@ void Cave::ConstructGeometry() shCaveTR1->DefineSection(0, -706. - 8.6, 0., 790.5); shCaveTR1->DefineSection(1, 707. + 7.6, 0., 790.5); TGeoTube* shCaveTR2 = new TGeoTube("shCaveTR2", 0., 150., 110.); + TGeoTube* shCaveTR3 = new TGeoTube("shCaveTR3", 0., 80., 75.); TGeoTranslation* transCaveTR2 = new TGeoTranslation("transTR2", 0, 30., -505. - 110.); + TGeoTranslation* transCaveTR3 = new TGeoTranslation("transTR3", 0, 30., 714.6 + 75.); transCaveTR2->RegisterYourself(); - TGeoCompositeShape* shCaveTR = new TGeoCompositeShape("shCaveTR", "shCaveTR1-shCaveTR2:transTR2"); + transCaveTR3->RegisterYourself(); + + TGeoCompositeShape* shCaveTR = new TGeoCompositeShape("shCaveTR", "shCaveTR1-shCaveTR2:transTR2+shCaveTR3:transTR3"); TGeoVolume* voBarrel = new TGeoVolume("barrel", shCaveTR, kMedAir); cavevol->AddNode(voBarrel, 1, new TGeoTranslation(0., -30., 0.)); if (mHasRB24) { // should be not true only for alice 3 // mother volume for RB24 side (FDD, Compensator) - const Float_t kRB24CL = 2. * 597.9; + const Float_t kRB24CL = 2. * 597.9 - 150.; auto shCaveRB24 = new TGeoPcon(0., 360., 6); - Float_t z0 = kRB24CL / 2 + 714.6; + Float_t z0 = kRB24CL / 2 + 714.6 + 150.; shCaveRB24->DefineSection(0, -kRB24CL / 2., 0., 105.); shCaveRB24->DefineSection(1, -z0 + 1705., 0., 105.); shCaveRB24->DefineSection(2, -z0 + 1705., 0., 14.5); - shCaveRB24->DefineSection(3, -z0 + 1880., 0., 14.5); - shCaveRB24->DefineSection(4, -z0 + 1880., 0., 40.0); - shCaveRB24->DefineSection(5, kRB24CL / 2, 0., 40.0); + shCaveRB24->DefineSection(3, -z0 + 1878, 0., 14.5); + shCaveRB24->DefineSection(4, -z0 + 1878., 0., 40.0); + shCaveRB24->DefineSection(5, kRB24CL / 2., 0., 40.0); TGeoVolume* caveRB24 = new TGeoVolume("caveRB24", shCaveRB24, kMedAir); caveRB24->SetVisibility(0); diff --git a/Detectors/Passive/src/Compensator.cxx b/Detectors/Passive/src/Compensator.cxx index 68e344495aaab..25b3e2a475340 100644 --- a/Detectors/Passive/src/Compensator.cxx +++ b/Detectors/Passive/src/Compensator.cxx @@ -110,7 +110,7 @@ void Compensator::ConstructGeometry() void Compensator::createCompensator() { auto top = gGeoManager->GetVolume("caveRB24"); - top->AddNode(createMagnetYoke(), 1, new TGeoTranslation(0., 0., 1075. - 1313.347)); + top->AddNode(createMagnetYoke(), 1, new TGeoTranslation(0., 0., 1000. - 1313.347)); } TGeoVolume* Compensator::createMagnetYoke() diff --git a/Detectors/Passive/src/ExternalModule.cxx b/Detectors/Passive/src/ExternalModule.cxx new file mode 100644 index 0000000000000..fc6bd6953b82d --- /dev/null +++ b/Detectors/Passive/src/ExternalModule.cxx @@ -0,0 +1,175 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel (CERN), 2026 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ClassImp(o2::passive::ExternalModule) + +namespace o2::passive +{ + +ExternalModule::ExternalModule(const char* name, const char* long_title, ExternalModuleOptions options) : PassiveBase(name, long_title), mOptions(options) +{ +} + +void ExternalModule::remapMedia(TGeoVolume* top_volume) +{ + std::unordered_map medium_ptr_mapping; + std::unordered_set volumes_already_treated; + int counter = 1; + + auto modulename = GetName(); + + // The transformer function + auto transform_media = [&](TGeoVolume* vol_) { + if (volumes_already_treated.find(vol_) != volumes_already_treated.end()) { + // this volume was already transformed + return; + } + volumes_already_treated.insert(vol_); + + if (dynamic_cast(vol_)) { + // do nothing for assemblies (they don't have a medium) + return; + } + + auto medium = vol_->GetMedium(); + if (!medium) { + return; + } + + auto iter = medium_ptr_mapping.find(medium); + if (iter != medium_ptr_mapping.end()) { + // This medium has already been transformed, so + // we just update the volume + vol_->SetMedium(iter->second); + return; + } else { + std::cout << "Transforming media with name " << medium->GetName() << " for volume " << vol_->GetName() << "\n"; + + // we found a medium, not yet treated + auto curr_mat = medium->GetMaterial(); + auto& matmgr = o2::base::MaterialManager::Instance(); + + matmgr.Material(modulename, counter, curr_mat->GetName(), curr_mat->GetA(), curr_mat->GetZ(), curr_mat->GetDensity(), curr_mat->GetRadLen(), curr_mat->GetIntLen()); + // TGeo medium params are stored in a flat array with the following convention + // fParams[0] = isvol; + // fParams[1] = ifield; + // fParams[2] = fieldm; + // fParams[3] = tmaxfd; + // fParams[4] = stemax; + // fParams[5] = deemax; + // fParams[6] = epsil; + // fParams[7] = stmin; + const auto isvol = medium->GetParam(0); + const auto isxfld = medium->GetParam(1); + const auto sxmgmx = medium->GetParam(2); + const auto tmaxfd = medium->GetParam(3); + const auto stemax = medium->GetParam(4); + const auto deemax = medium->GetParam(5); + const auto epsil = medium->GetParam(6); + const auto stmin = medium->GetParam(7); + + matmgr.Medium(modulename, counter, medium->GetName(), counter, isvol, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + + // there will be new Material and Medium objects; fetch them + auto new_med = matmgr.getTGeoMedium(modulename, counter); + + // insert into cache + medium_ptr_mapping[medium] = new_med; + vol_->SetMedium(new_med); + counter++; + } + }; // end transformer lambda + + // a generic volume walker + std::function visit_volume; + visit_volume = [&](TGeoVolume* vol) -> void { + if (!vol) { + return; + } + + // call the transformer + transform_media(vol); + + // Recurse into daughters + const int nd = vol->GetNdaughters(); + for (int i = 0; i < nd; ++i) { + TGeoNode* node = vol->GetNode(i); + if (!node) { + continue; + } + TGeoVolume* child = node->GetVolume(); + if (!child) { + continue; + } + + visit_volume(child); + } + }; + + visit_volume(top_volume); +} + +void ExternalModule::ConstructGeometry() +{ + // JIT the geom builder hook + if (!initGeomBuilderHook()) { + LOG(error) << " Could not load geometry builder hook"; + return; + } + + // otherwise execute it and obtain pointer to top most module volume + auto module_top = mGeomHook(); + if (!module_top) { + LOG(error) << "No module found\n"; + return; + } + + remapMedia(const_cast(module_top)); + + // place it into the provided anchor volume (needs to exist) + auto anchor = gGeoManager->FindVolumeFast(mOptions.anchor_volume.c_str()); + if (!anchor) { + LOG(error) << "Anchor volume " << mOptions.anchor_volume << " not found. Aborting"; + return; + } + anchor->AddNode(const_cast(module_top), 1, const_cast(mOptions.placement)); +} + +bool ExternalModule::initGeomBuilderHook() +{ + if (mOptions.root_macro_file.size() > 0) { + LOG(info) << "Initializing the hook for geometry module building"; + auto expandedHookFileName = o2::utils::expandShellVarsInFileName(mOptions.root_macro_file); + if (std::filesystem::exists(expandedHookFileName)) { + // if this file exists we will compile the hook on the fly (the last one is an identifier --> maybe make it dependent on this class) + mGeomHook = o2::conf::GetFromMacro(mOptions.root_macro_file, "get_builder_hook_unchecked()", "function", "o2_passive_extmodule_builder"); + LOG(info) << "Hook initialized from file " << expandedHookFileName; + return true; + } + } + return false; +} + +} // namespace o2::passive \ No newline at end of file diff --git a/Detectors/Passive/src/Pipe.cxx b/Detectors/Passive/src/Pipe.cxx index 56f6429bc73c8..56ccfc45f0b89 100644 --- a/Detectors/Passive/src/Pipe.cxx +++ b/Detectors/Passive/src/Pipe.cxx @@ -786,13 +786,13 @@ void Pipe::ConstructGeometry() // Copper Tube RB24/1 const Float_t kRB24CuTubeL = 381.5; - const Float_t kRB24cCuTubeL = 155.775; + const Float_t kRB24cCuTubeL = 155.775 - 150.; const Float_t kRB24bCuTubeL = kRB24CuTubeL - kRB24cCuTubeL; const Float_t kRB24CuTubeRi = 8.0 / 2.; const Float_t kRB24CuTubeRo = 8.4 / 2.; const Float_t kRB24CuTubeFRo = 7.6; const Float_t kRB24CuTubeFL = 1.86; - const Float_t kRB24CL = 2. * 597.9; + const Float_t kRB24CL = 2. * 597.9 - 150.; // // introduce cut at end of barrel 714.6m // @@ -812,7 +812,7 @@ void Pipe::ConstructGeometry() voRB24cCuTubeM->AddNode(voRB24cCuTube, 1, gGeoIdentity); // Air outside tube with higher transport cuts - TGeoVolume* voRB24CuTubeA = new TGeoVolume("voRB24CuTubeA", new TGeoTube(80., 81., kRB24bCuTubeL / 2.), kMedAirHigh); + TGeoVolume* voRB24CuTubeA = new TGeoVolume("voRB24CuTubeA", new TGeoTube(79., 80., kRB24bCuTubeL / 2.), kMedAirHigh); voRB24CuTubeA->SetVisibility(0); // Simplified DN 100 Flange diff --git a/Detectors/Passive/src/PipeRun4.cxx b/Detectors/Passive/src/PipeRun4.cxx index 7a2ff6dcfe90b..5aa0b63a6ac78 100644 --- a/Detectors/Passive/src/PipeRun4.cxx +++ b/Detectors/Passive/src/PipeRun4.cxx @@ -215,8 +215,7 @@ void PipeRun4::ConstructGeometry() voberylliumTube->SetLineColor(kRed); TGeoTube* berylliumTubeVacuum = - new TGeoTube("IP_PIPEVACUUMsh", 0., kBeryliumSectionOuterRadius - kBeryliumSectionThickness, - (kBeryliumSectionZmax - kBeryliumSectionZmin) / 2); + new TGeoTube("IP_PIPEVACUUMsh", 0., kBeryliumSectionOuterRadius, (kBeryliumSectionZmax - kBeryliumSectionZmin) / 2); TGeoVolume* voberylliumTubeVacuum = new TGeoVolume("IP_PIPEMOTHER", berylliumTubeVacuum, kMedVac); voberylliumTubeVacuum->AddNode(voberylliumTube, 1, gGeoIdentity); voberylliumTubeVacuum->SetVisibility(0); @@ -643,7 +642,7 @@ void PipeRun4::ConstructGeometry() // Drawings from C. Gargiulo : // \\cern.ch\dfs\Workspaces\c\cgargiul\EXPERIMENT\ALICE\ALICE_MECHANICS\ALICE_DATA_PACKAGE\IN\DETECTORS\ITS_UPGRADE\1-DESIGN\0-IF_Control_Drawing\20140207_ICD_ITS_MFT_BP ///////////////////////////////////////////////////////////////////// - + TGeoVolumeAssembly* beamPipeAsideSection = new TGeoVolumeAssembly("BeamPipeAsideSection"); float kConicalBerilliumMinThickness = 0.08; float kConicalBerilliumMaxThickness = 0.1; float kFlangeZ = 483.75; @@ -657,33 +656,34 @@ void PipeRun4::ConstructGeometry() float kConicalBePipeEndOuterRadius = 3.0; TGeoPcon* tube0 = new TGeoPcon(0., 360., 5); - tube0->DefineSection(0, kFlangeZ - kFlangeWidth / 2, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - tube0->DefineSection(1, kConicalBerylliumEnd, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); + tube0->DefineSection(0, kFlangeZ - kFlangeWidth / 2, kConicalBePipeEndOuterRadius - kConicalBerilliumMinThickness, kConicalBePipeEndOuterRadius); + tube0->DefineSection(1, kConicalBerylliumEnd, kConicalBePipeEndOuterRadius - kConicalBerilliumMinThickness, kConicalBePipeEndOuterRadius); tube0->DefineSection(2, kSupport1 + kSupportWidth, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness, kPipeRadiusAtSupport1); tube0->DefineSection(3, kSupport1, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness, kPipeRadiusAtSupport1); tube0->DefineSection(4, kBeryliumSectionZmax, kBeryliumSectionOuterRadius - kConicalBerilliumMinThickness, kBeryliumSectionOuterRadius); // need a transition to kConicalBerilliumMaxThickness - TGeoPcon* tube0vide = new TGeoPcon(0., 360., 5); - tube0vide->DefineSection(0, kFlangeZ - kFlangeWidth / 2, 0., kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - tube0vide->DefineSection(1, kConicalBerylliumEnd, 0., kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - tube0vide->DefineSection(2, kSupport1 + kSupportWidth, 0, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness - 0.01); - tube0vide->DefineSection(3, kSupport1, 0, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness - 0.01); - tube0vide->DefineSection(4, kBeryliumSectionZmax, 0., kBeryliumSectionOuterRadius - kConicalBerilliumMinThickness - 0.01); + TGeoPcon* tube0Mo = new TGeoPcon(0., 360., 5); + tube0Mo->DefineSection(0, kFlangeZ - kFlangeWidth / 2, 0., kConicalBePipeEndOuterRadius); + tube0Mo->DefineSection(1, kConicalBerylliumEnd, 0., kConicalBePipeEndOuterRadius); + tube0Mo->DefineSection(2, kSupport1 + kSupportWidth, 0, kPipeRadiusAtSupport1); + tube0Mo->DefineSection(3, kSupport1, 0, kPipeRadiusAtSupport1); + tube0Mo->DefineSection(4, kBeryliumSectionZmax, 0., kBeryliumSectionOuterRadius); TGeoVolume* votube0 = new TGeoVolume("votube0", tube0, kMedBe); votube0->SetLineColor(kRed); - TGeoVolume* votube0vide = new TGeoVolume("votube0vide", tube0vide, kMedVac); - votube0vide->SetLineColor(kGreen); + TGeoVolume* votube0Mo = new TGeoVolume("votube0Mo", tube0Mo, kMedVac); + votube0Mo->AddNode(votube0, 1, gGeoIdentity); + votube0Mo->SetLineColor(kGreen); - barrel->AddNode(votube0, 1, new TGeoTranslation(0., 30., 0.)); - barrel->AddNode(votube0vide, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(votube0Mo, 1, gGeoIdentity); - TGeoVolume* beampipeSupportA1 = makeSupportBar("A1", kPipeRadiusAtSupport1 + 0.01, kPipeRadiusAtSupport1 + 0.38, 20.67, 14.25); - barrel->AddNode(beampipeSupportA1, 1, new TGeoTranslation(0., 30, kSupport1 + kSupportWidth / 2.)); + // already defined in IT3 + // TGeoVolume* beampipeSupportA1 = makeSupportBar("A1", kPipeRadiusAtSupport1 + 0.01, kPipeRadiusAtSupport1 + 0.38, 20.67, 14.25); + // beamPipeAsideSection->AddNode(beampipeSupportA1, 1, new TGeoTranslation(0., 0., kSupport1 + kSupportWidth / 2.)); // Length is approximate TGeoVolume* beampipeSupportA2 = makeSupportBar("A2", kConicalBePipeEndOuterRadius, kConicalBePipeEndOuterRadius + 0.38, 44, 37.5); - barrel->AddNode(beampipeSupportA2, 1, new TGeoTranslation(0., 30, kConicalBerylliumEnd + kSupportWidth / 2.)); + beamPipeAsideSection->AddNode(beampipeSupportA2, 1, new TGeoTranslation(0., 0., kConicalBerylliumEnd + kSupportWidth / 2.)); TGeoPcon* Bolt1 = new TGeoPcon(0., 360, 8); Bolt1->DefineSection(0, 0, 0, 0.5); @@ -735,7 +735,7 @@ void PipeRun4::ConstructGeometry() Bolts->AddNode(volBolt1, 7, t7); Bolts->AddNode(volBolt1, 8, t8); - barrel->AddNode(Bolts, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(Bolts, 1, gGeoIdentity); TGeoTranslation* Tflange = new TGeoTranslation(0, 0, kFlangeZ); Tflange->SetName("Tflange"); @@ -754,53 +754,40 @@ void PipeRun4::ConstructGeometry() TGeoVolume* volflange = new TGeoVolume("voFlangeHoles", FlangeWithHoles, kMedAlBe); volflange->SetLineWidth(2); volflange->SetLineColor(kGray); - - barrel->AddNode(volflange, 1, new TGeoTranslation(0., 30., 0.)); - - TGeoPcon* pipeSamell = new TGeoPcon(0., 360., 2); - pipeSamell->DefineSection(0, kFlangeZ + kFlangeWidth / 2, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - pipeSamell->DefineSection(1, kFlangeZ + 5.13 + 0.435 + 0.4 + 0.08, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - pipeSamell->SetName("pipeSamell"); - - TGeoVolume* VolpipeSmall = new TGeoVolume("voPipeSmallVac", pipeSamell, kMedAlu2219); - VolpipeSmall->SetLineWidth(2); - barrel->AddNode(VolpipeSmall, 1, new TGeoTranslation(0., 30., 0.)); - - TGeoPcon* pipeSmallVac = new TGeoPcon(0., 360., 2); - pipeSmallVac->DefineSection(0, kFlangeZ + kFlangeWidth / 2, 0, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - pipeSmallVac->DefineSection(1, kFlangeZ + 5.13 + 0.435 + 0.4 + 0.08, 0, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - TGeoVolume* vopipeSmallVac = new TGeoVolume("voPipeSmallVac", pipeSmallVac, kMedVac); - vopipeSmallVac->SetLineColor(kGreen); - - barrel->AddNode(vopipeSmallVac, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(volflange, 1, gGeoIdentity); // -- Bellows on A side - // float plieradius = (3.72 + (2. * 7 - 2.) * 0.03) / (4. * 7); // radius of bellows "plis" float plieradiusA = 0.2; // radius of bellow plies - // ------------------ First Bellow -------------------- // Inner: 3.0 cm, outer 3.97 cm length 8.47 cm with 10 wiggles - // check meaning of dU ; it is probably the total length, see also below - TGeoVolume* vobellows1A = makeBellow("bellows1A", 10, 3.0, 3.97, 8.47, plieradiusA, 0.03); - // Z position is rough for now. - barrel->AddNode(vobellows1A, 1, new TGeoTranslation(0., 30., kFlangeZ + 10)); + TGeoVolume* vobellows1A = makeBellow("bellows1A", 10, 3.0, 3.97, plieradiusA, 0.03); + Float_t dU = (static_cast(vobellows1A->GetShape()))->GetDZ(); + beamPipeAsideSection->AddNode(vobellows1A, 1, new TGeoTranslation(0., 0., kFlangeZ + 2. * dU)); // Comments: removing 1/2 plie (see makeBellow): 0.31= 2*0.17-0.03 and 0.08: free space + Float_t pipeSmallDz = (dU - kFlangeWidth / 2.) / 2.; + TGeoTube* pipeSmall = new TGeoTube("pipeSmall", kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius, pipeSmallDz); + TGeoVolume* vopipeSmall = new TGeoVolume("voPipeSmall", pipeSmall, kMedAlu2219); + vopipeSmall->SetLineWidth(2); - // ------------------ Outer pipe after flange -------------------- - TGeoPcon* pipeOut = new TGeoPcon(0., 360., 2); - pipeOut->DefineSection(0, kFlangeZ + 13.6 - 0.08, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - pipeOut->DefineSection(1, 714.6, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); + TGeoTube* pipeSmallMo = new TGeoTube(0., kConicalBePipeEndOuterRadius, pipeSmallDz); + TGeoVolume* vopipeSmallMo = new TGeoVolume("voPipeSmallMo", pipeSmallMo, kMedVac); + vopipeSmallMo->SetLineColor(kGreen); + vopipeSmallMo->AddNode(vopipeSmall, 1, gGeoIdentity); - TGeoVolume* OuterPIPE = new TGeoVolume("pipeOut", pipeOut, kMedAlu2219); - barrel->AddNode(OuterPIPE, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(vopipeSmallMo, 1, new TGeoTranslation(0., 0., kFlangeZ + kFlangeWidth / 2. + pipeSmallDz)); - // The end of the barrel volume is at 714.6 cm, after that we start with RB24 volume - TGeoPcon* pipeOutVac = new TGeoPcon(0., 360., 2); - pipeOutVac->DefineSection(0, kFlangeZ + 13.6 - 0.08, 0, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness); - pipeOutVac->DefineSection(1, 714.6, 0., kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness); + // ------------------ Outer pipe after flange -------------------- + // The end of the barrel volume is at 864.6 cm, after that we start with RB24 volume + Float_t pipeEndZ = 864.6; + Float_t pipeOutDz = (pipeEndZ - (kFlangeZ + 3. * dU)) / 2.; + TGeoTube* pipeOut = new TGeoTube(kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius, pipeOutDz); + TGeoVolume* OuterPIPE = new TGeoVolume("pipeOut", pipeOut, kMedAlu2219); - TGeoVolume* OuterPIPEVac = new TGeoVolume("pipeOutVac", pipeOutVac, kMedAlu2219); - barrel->AddNode(OuterPIPEVac, 1, new TGeoTranslation(0., 30., 0.)); + TGeoTube* pipeOutMo = new TGeoTube(0., kConicalBePipeEndOuterRadius, pipeOutDz); + TGeoVolume* OuterPIPEMo = new TGeoVolume("pipeOutMo", pipeOutMo, kMedVac); + OuterPIPEMo->AddNode(OuterPIPE, 1, gGeoIdentity); + beamPipeAsideSection->AddNode(OuterPIPEMo, 1, new TGeoTranslation(0., 0., pipeEndZ - pipeOutDz)); + barrel->AddNode(beamPipeAsideSection, 1, new TGeoTranslation(0., 30., 0.)); //------------------------------------------------- @@ -823,19 +810,19 @@ void PipeRun4::ConstructGeometry() // Copper Tube RB24/1 const float kRB24CuTubeL = 381.5; - const float kRB24cCuTubeL = 155.775 + (28.375 - 18.135); + const float kRB24cCuTubeL = 155.775 - 150.; const float kRB24bCuTubeL = kRB24CuTubeL - kRB24cCuTubeL; const float kRB24CuTubeRi = 5.8 / 2.; const float kRB24CuTubeRo = 6.0 / 2.; const float kRB24CuTubeFRo = 7.6; const float kRB24CuTubeFL = 1.86; - const float kRB24CL = 2. * 597.9; + const float kRB24CL = 2. * 597.9 - 150.; // // introduce cut at end of barrel 714.6m // // outside barrel - TGeoVolume* voRB24cCuTubeM = new TGeoVolume("voRB24cCuTubeM", new TGeoTube(0., kRB24CuTubeRi, kRB24cCuTubeL / 2.), kMedVacNFHC); + TGeoVolume* voRB24cCuTubeM = new TGeoVolume("voRB24cCuTubeM", new TGeoTube(0., kRB24CuTubeRo, kRB24cCuTubeL / 2.), kMedVacNFHC); TGeoVolume* voRB24cCuTube = new TGeoVolume("voRB24cCuTube", new TGeoTube(kRB24CuTubeRi, kRB24CuTubeRo, kRB24cCuTubeL / 2.), kMedAlu2219); voRB24cCuTubeM->AddNode(voRB24cCuTube, 1, gGeoIdentity); @@ -877,7 +864,7 @@ void PipeRun4::ConstructGeometry() const float kRB24B1PlieThickness = 0.015; // Plie thickness const float kRB24B1PlieRadius = - (kRB24B1BellowUndL + (2. * kRB24B1NumberOfPlies - 2.) * kRB24B1PlieThickness) / (4. * kRB24B1NumberOfPlies); + (kRB24B1BellowUndL + 2. * kRB24B1NumberOfPlies * kRB24B1PlieThickness) / (4. * kRB24B1NumberOfPlies + 2.); const float kRB24B1ProtTubeThickness = 0.02; // Thickness of the protection tube const float kRB24B1ProtTubeLength = 4.2; // Length of the protection tube @@ -893,7 +880,7 @@ void PipeRun4::ConstructGeometry() // // Bellow Section TGeoVolume* voRB24B1Bellow = makeBellow("RB24B1", kRB24B1NumberOfPlies, kRB24B1BellowRi, kRB24B1BellowRo, - kRB24B1BellowUndL, kRB24B1PlieRadius, kRB24B1PlieThickness); + kRB24B1PlieRadius, kRB24B1PlieThickness); voRB24B1Bellow->SetVisibility(0); float newRB24B1BellowUndL = 2 * (static_cast(voRB24B1Bellow->GetShape()))->GetDz(); @@ -2841,13 +2828,11 @@ TGeoPcon* PipeRun4::makeInsulationFromTemplate(TGeoPcon* shape) return insu; } -TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax, float dU, float rPlie, - float dPlie) +TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax, float rPlie, float dPlie) { // nc Number of convolution // rMin Inner radius of the bellow // rMax Outer radius of the bellow - // dU Undulation length // rPlie Plie radius // dPlie Plie thickness auto& matmgr = o2::base::MaterialManager::Instance(); @@ -2897,10 +2882,10 @@ TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax asWiggle->AddNode(voWiggleL, 1, new TGeoTranslation(0., 0., z0)); asWiggle->GetShape()->ComputeBBox(); // enforce recomputing of BBox // - float zBellowTot = nc * (static_cast(asWiggle->GetShape()))->GetDZ(); - TGeoVolume* voBellow = new TGeoVolume(fmt::format("{:s}BellowUS", ext).c_str(), new TGeoTube(rMin, rMax, zBellowTot), kMedVac); + float zBellowTot = nc * (2. * (static_cast(asWiggle->GetShape()))->GetDZ() - dPlie) + 2. * rPlie; + TGeoVolume* voBellow = new TGeoVolume(fmt::format("{:s}BellowUS", ext).c_str(), new TGeoTube(rMin, rMax, zBellowTot / 2.), kMedVac); // Positioning of the volumes - z0 = -dU / 2. + rPlie; + z0 = -zBellowTot / 2. + rPlie; voBellow->AddNode(voWiggleL, 2, new TGeoTranslation(0., 0., z0)); z0 += rPlie; float zsh = 4. * rPlie - 2. * dPlie; @@ -2908,6 +2893,7 @@ TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax float zpos = z0 + iw * zsh; voBellow->AddNode(asWiggle, iw + 1, new TGeoTranslation(0., 0., zpos - dPlie)); } + return voBellow; } diff --git a/Detectors/Raw/TFReaderDD/CMakeLists.txt b/Detectors/Raw/TFReaderDD/CMakeLists.txt index e58f0d50115f7..12ecc9ca8795d 100644 --- a/Detectors/Raw/TFReaderDD/CMakeLists.txt +++ b/Detectors/Raw/TFReaderDD/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(TFReaderDD O2::Headers O2::Framework O2::DetectorsRaw + O2::DataFormatsParameters O2::CommonUtils O2::Algorithm FairMQ::FairMQ) diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index 07a62a7fd4a58..2b8090af42648 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -31,8 +31,14 @@ #include "TFReaderSpec.h" #include "TFReaderDD/SubTimeFrameFileReader.h" #include "TFReaderDD/SubTimeFrameFile.h" +#include "CommonUtils/StringUtils.h" #include "CommonUtils/FileFetcher.h" #include "CommonUtils/FIFO.h" +#include "CommonUtils/IRFrameSelector.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" +#include "Algorithm/RangeTokenizer.h" #include #include #include @@ -66,6 +72,8 @@ class TFReaderSpec : public o2f::Task void endOfStream(o2f::EndOfStreamContext& ec) final; private: + void loadRunTimeSpans(const std::string& flname); + void runTimeRangesToIRFrameSelector(int runNumber); void stopProcessing(o2f::ProcessingContext& ctx); void TFBuilder(); @@ -76,9 +84,14 @@ class TFReaderSpec : public o2f::Task o2::utils::FIFO> mTFQueue{}; // queued TFs // std::unordered_map> mSeenOutputMap; std::unordered_map mSeenOutputMap; - int mTFCounter = 0; + std::map>> mRunTimeRanges; + o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector + int mConvRunTimeRangesToOrbits = -1; // not defined yet + int mSentTFCounter = 0; + int mAccTFCounter = 0; int mTFBuilderCounter = 0; int mNWaits = 0; + int mTFLength = 32; long mTotalWaitTime = 0; size_t mSelIDEntry = 0; // next TFID to select from the mInput.tfIDs (if non-empty) bool mRunning = false; @@ -105,6 +118,9 @@ void TFReaderSpec::init(o2f::InitContext& ic) mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mInput.maxTFCache = std::max(1, ic.options().get("max-cached-tf")); mInput.maxFileCache = std::max(1, ic.options().get("max-cached-files")); + if (!mInput.fileRunTimeSpans.empty()) { + loadRunTimeSpans(mInput.fileRunTimeSpans); + } mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); @@ -142,21 +158,17 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) if (verbose && mInput.verbosity > 0) { LOGP(info, "Acknowledge: part {}/{} {}/{}/{:#x} size:{} split {}/{}", ip, np, hd->dataOrigin.as(), hd->dataDescription.as(), hd->subSpecification, msgh.GetSize() + parts[ip + 1].GetSize(), hd->splitPayloadIndex, hd->splitPayloadParts); } - if (dph->startTime != this->mTFCounter) { - LOGP(fatal, "Local tf counter {} != TF timeslice {} for {}", this->mTFCounter, dph->startTime, - o2::framework::DataSpecUtils::describe(o2::framework::OutputSpec{hd->dataOrigin, hd->dataDescription, hd->subSpecification})); - } if (hd->splitPayloadIndex == 0) { // check the 1st one only auto& entry = this->mSeenOutputMap[{hd->dataDescription.str, hd->dataOrigin.str}]; - if (entry.count != this->mTFCounter) { + if (entry.count != this->mSentTFCounter) { if (verbose && hdPrev) { // report previous partition size LOGP(info, "Block:{} {}/{} with size {}", nblocks, hdPrev->dataOrigin.as(), hdPrev->dataDescription.as(), dsize); } dsizeTot += dsize; dsize = 0; - entry.count = this->mTFCounter; // acknowledge identifier seen in the data + entry.count = this->mSentTFCounter; // acknowledge identifier seen in the data LOG(debug) << "Found a part " << ip << " of " << np << " | " << hd->dataOrigin.as() << "/" << hd->dataDescription.as() - << "/" << hd->subSpecification << " part " << hd->splitPayloadIndex << " of " << hd->splitPayloadParts << " for TF " << this->mTFCounter; + << "/" << hd->subSpecification << " part " << hd->splitPayloadIndex << " of " << hd->splitPayloadParts << " for TF " << this->mSentTFCounter; nblocks++; } } @@ -208,11 +220,11 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) const auto* hd0 = o2h::get(dataptr); const auto* dph = o2h::get(dataptr); for (auto& out : this->mSeenOutputMap) { - if (out.second.count == this->mTFCounter) { // was seen in the data + if (out.second.count == this->mSentTFCounter) { // was seen in the data continue; } LOG(debug) << "Adding dummy output for " << out.first.dataOrigin.as() << "/" << out.first.dataDescription.as() - << "/" << out.second.defSubSpec << " for TF " << this->mTFCounter; + << "/" << out.second.defSubSpec << " for TF " << this->mSentTFCounter; o2h::DataHeader outHeader(out.first.dataDescription, out.first.dataOrigin, out.second.defSubSpec, 0); outHeader.payloadSerializationMethod = o2h::gSerializationMethodNone; outHeader.firstTForbit = hd0->firstTForbit; @@ -259,7 +271,7 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) auto tNow = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); auto tDiff = tNow - tLastTF; - if (mTFCounter && tDiff < mInput.delay_us) { + if (mSentTFCounter && tDiff < mInput.delay_us) { std::this_thread::sleep_for(std::chrono::microseconds((size_t)(mInput.delay_us - tDiff))); // respect requested delay before sending } for (auto& msgIt : *tfPtr.get()) { @@ -274,9 +286,9 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) // however this is a small enough hack for now. ctx.services().get().fakeDispatch(); tNow = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - LOGP(info, "Sent TF {} of size {} with {} parts, {:.4f} s elapsed from previous TF., WaitSending={}", mTFCounter, dataSize, nparts, mTFCounter ? double(tNow - tLastTF) * 1e-6 : 0., mWaitSendingLast); + LOGP(info, "Sent TF {} of size {} with {} parts, {:.4f} s elapsed from previous TF., WaitSending={}", mSentTFCounter, dataSize, nparts, mSentTFCounter ? double(tNow - tLastTF) * 1e-6 : 0., mWaitSendingLast); tLastTF = tNow; - ++mTFCounter; + ++mSentTFCounter; while (mTFQueue.size() == 0 && mWaitSendingLast) { usleep(10000); @@ -289,7 +301,7 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) } // usleep(5000); // wait 5ms for new TF to be built } - if (mTFCounter >= mInput.maxTFs || (!mTFQueue.size() && !mRunning)) { // done + if (mSentTFCounter >= mInput.maxTFs || (!mTFQueue.size() && !mRunning)) { // done stopProcessing(ctx); } } @@ -314,7 +326,7 @@ void TFReaderSpec::stopProcessing(o2f::ProcessingContext& ctx) return; } stopDone = true; - LOGP(info, "{} TFs in {} loops were sent, spent {:.2} s in {} data waiting states", mTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); + LOGP(info, "{} TFs in {} loops were sent, spent {:.2} s in {} data waiting states", mSentTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; if (mFileFetcher) { mFileFetcher->stop(); @@ -409,27 +421,46 @@ void TFReaderSpec::TFBuilder() std::this_thread::sleep_for(sleepTime); continue; } - auto tf = reader.read(mDevice, mOutputRoutes, mInput.rawChannelConfig, mSelIDEntry, mInput.sup0xccdb, mInput.verbosity); + auto tf = reader.read(mDevice, mOutputRoutes, mInput.rawChannelConfig, mAccTFCounter, mInput.sup0xccdb, mInput.verbosity); bool acceptTF = true; if (tf) { + if (mRunTimeRanges.size()) { + const auto* dataptr = (*tf->begin()->second.get())[0].GetData(); + const auto* hd0 = o2h::get(dataptr); + static int runNumberPrev = -1; + if (runNumberPrev != hd0->runNumber) { + runNumberPrev = hd0->runNumber; + runTimeRangesToIRFrameSelector(runNumberPrev); + } + if (mIRFrameSelector.isSet()) { + o2::InteractionRecord ir0(0, hd0->firstTForbit); + o2::InteractionRecord ir1(o2::constants::lhc::LHCMaxBunches - 1, hd0->firstTForbit < 0xffffffff - (mTFLength - 1) ? hd0->firstTForbit + (mTFLength - 1) : 0xffffffff); + auto irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); + acceptTF = (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(), acceptTF ? "" : "do not ", mInput.invertIRFramesSelection ? "ON" : "OFF"); + } + } locID++; - if (!mInput.tfIDs.empty()) { + if (!mInput.tfIDs.empty() && acceptTF) { acceptTF = false; + while ((mInput.tfIDs[mSelIDEntry] < mTFBuilderCounter) && (mSelIDEntry + 1) < mInput.tfIDs.size()) { + mSelIDEntry++; + } + LOGP(info, "chec if mInput.tfIDs[{}]({}) == {}", mSelIDEntry, mInput.tfIDs[mSelIDEntry], mTFBuilderCounter); if (mInput.tfIDs[mSelIDEntry] == mTFBuilderCounter) { mWaitSendingLast = false; acceptTF = true; LOGP(info, "Retrieved TF#{} will be pushed as slice {} following user request", mTFBuilderCounter, mSelIDEntry); - mSelIDEntry++; } else { LOGP(info, "Retrieved TF#{} will be discared following user request", mTFBuilderCounter); } - } else { - mSelIDEntry++; } mTFBuilderCounter++; } if (mRunning && tf) { if (acceptTF) { + mAccTFCounter++; mWaitSendingLast = true; mTFQueue.push(std::move(tf)); } @@ -448,6 +479,110 @@ void TFReaderSpec::TFBuilder() } } +//_________________________________________________________ +void TFReaderSpec::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 TFReaderSpec::runTimeRangesToIRFrameSelector(int runNumber) +{ + // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit + mIRFrameSelector.clear(); + auto ent = mRunTimeRanges.find(runNumber); + if (ent == mRunTimeRanges.end()) { + LOGP(info, "RunTimeRanges selection was provided but run {} has no entries, all TFs will be processed", runNumber); + return; + } + o2::parameters::AggregatedRunInfo rinfo; + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + rinfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb, runNumber); + if (rinfo.runNumber != runNumber || rinfo.orbitsPerTF < 1) { + LOGP(fatal, "failed to extract AggregatedRunInfo for run {}", 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 (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(o2::InteractionRecord{0, uint32_t(orbMin)}, o2::InteractionRecord{o2::constants::lhc::LHCMaxBunches, uint32_t(orbMax)}); + } + mIRFrameSelector.setOwnList(frames, true); +} + //_________________________________________________________ o2f::DataProcessorSpec o2::rawdd::getTFReaderSpec(o2::rawdd::TFReaderInp& rinp) { diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h index e3a5b5c920010..9db18768c1bfe 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h @@ -32,6 +32,7 @@ struct TFReaderInp { std::string tffileRegex{}; std::string remoteRegex{}; std::string metricChannel{}; + std::string fileRunTimeSpans{}; o2::detectors::DetID::mask_t detMask{}; o2::detectors::DetID::mask_t detMaskRawOnly{}; o2::detectors::DetID::mask_t detMaskNonRawOnly{}; @@ -46,6 +47,7 @@ struct TFReaderInp { int maxTFsPerFile = -1; bool sendDummyForMissing = true; bool sup0xccdb = false; + bool invertIRFramesSelection = false; std::vector hdVec; std::vector tfIDs{}; }; diff --git a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx index 7d8ee09fe474f..bc682127b0d3f 100644 --- a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx +++ b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx @@ -39,6 +39,8 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"disable-dummy-output", VariantType::Bool, false, {"Disable sending empty output if corresponding data is not found in the data"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}); options.push_back(ConfigParamSpec{"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}); + 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{"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{"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}); // options for error-check suppression @@ -80,7 +82,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (rateLimitingIPCID > -1 && !chanFmt.empty()) { rinp.metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); } - + rinp.fileRunTimeSpans = configcontext.options().get("run-time-span-file"); + rinp.invertIRFramesSelection = configcontext.options().get("invert-irframe-selection"); WorkflowSpec specs; specs.emplace_back(o2::rawdd::getTFReaderSpec(rinp)); return specs; diff --git a/Detectors/TOF/base/include/TOFBase/Geo.h b/Detectors/TOF/base/include/TOFBase/Geo.h index 761528d472d71..24e8fbdf51174 100644 --- a/Detectors/TOF/base/include/TOFBase/Geo.h +++ b/Detectors/TOF/base/include/TOFBase/Geo.h @@ -66,6 +66,8 @@ class Geo static void antiRotateToSector(Float_t* xyz, Int_t isector); static void antiRotateToStrip(Float_t* xyz, Int_t iplate, Int_t istrip, Int_t isector); + static void alignedToNominalSector(Float_t* xyz, Int_t isector); + static void antiRotate(Float_t* xyz, Double_t rotationAngles[6]); static void getDetID(Float_t* pos, Int_t* det); static Int_t getIndex(const Int_t* detId); // Get channel index from det Id (for calibration mainly) diff --git a/Detectors/TOF/base/src/Geo.cxx b/Detectors/TOF/base/src/Geo.cxx index c76e6b4d83943..7df4c3a3537e1 100644 --- a/Detectors/TOF/base/src/Geo.cxx +++ b/Detectors/TOF/base/src/Geo.cxx @@ -988,6 +988,7 @@ void Geo::translate(Float_t* xyz, Float_t translationVector[3]) return; } + void Geo::translate(Float_t& x, Float_t& y, Float_t& z, Float_t translationVector[3]) { // @@ -1045,6 +1046,18 @@ void Geo::rotateToSector(Float_t* xyz, Int_t isector) return; } +void Geo::alignedToNominalSector(Float_t* xyz, Int_t isector) +{ + // rotate from the aligned sector frame coordinates to nominal ones (i.e. alpha=20*sector+10 deg.) + constexpr float CS[18] = {+9.848078e-01, +8.660254e-01, +6.427876e-01, +3.420201e-01, +6.123234e-17, -3.420201e-01, -6.427876e-01, -8.660254e-01, -9.848078e-01, -9.848078e-01, -8.660254e-01, -6.427876e-01, -3.420201e-01, -1.836970e-16, +3.420201e-01, +6.427876e-01, +8.660254e-01, +9.848078e-01}; + constexpr float SN[18] = {+1.736482e-01, +5.000000e-01, +7.660444e-01, +9.396926e-01, +1.000000e+00, +9.396926e-01, +7.660444e-01, +5.000000e-01, +1.736482e-01, -1.736482e-01, -5.000000e-01, -7.660444e-01, -9.396926e-01, -1.000000e+00, -9.396926e-01, -7.660444e-01, -5.000000e-01, -1.736482e-01}; + Float_t xyzDummy[3] = {xyz[1], xyz[2], xyz[0]}; // go to twisted coordinates... + o2::tof::Geo::antiRotateToSector(xyzDummy, isector); // lab coordinates + xyz[0] = xyzDummy[0] * CS[isector] + xyzDummy[1] * SN[isector]; + xyz[1] = -xyzDummy[0] * SN[isector] + xyzDummy[1] * CS[isector]; + xyz[2] = xyzDummy[2]; +} + void Geo::antiRotateToStrip(Float_t* xyz, Int_t iplate, Int_t istrip, Int_t isector) { Float_t xyzDummy[3] = {0., 0., 0.}; diff --git a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h index aaab8a06e5e86..4c8f5cdae8654 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h @@ -99,6 +99,8 @@ class LHCClockCalibrator final : public o2::calibration::TimeSlotCalibration("nbins")); auto slotL = ic.options().get("tf-per-slot"); auto delay = ic.options().get("max-delay"); + std::string path = ic.options().get("output-path"); + mCalibrator = std::make_unique(minEnt, nb); + mCalibrator->setPath(path.data()); mCalibrator->setSlotLength(slotL); mCalibrator->setMaxSlotsDelay(delay); @@ -216,6 +219,7 @@ DataProcessorSpec getLHCClockCalibDeviceSpec(bool useCCDB) AlgorithmSpec{adaptFromTask(ccdbRequest, useCCDB)}, Options{ {"tf-per-slot", VariantType::UInt32, 5u, {"number of TFs per calibration time slot"}}, + {"output-path", VariantType::String, "TOF/Calib/LHCphaseSync", {"path to ccdb output"}}, {"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"}}, {"nbins", VariantType::Int, 4000, {"number of bins for "}}}}; diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h index e559dcce7a1da..53cdf59d08572 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -31,10 +31,10 @@ namespace o2 namespace tof { -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::TOF) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TOF, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h index c09aa6abc9f7b..714b23d955a78 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h @@ -29,7 +29,7 @@ namespace tof 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; @@ -43,7 +43,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 tof } // namespace o2 diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h index ee0739c076597..27377b6447d1c 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace tof 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; @@ -43,7 +43,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 tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx index 400914c64021f..8c0445e3ee3cb 100644 --- a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace tof { - -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(); @@ -93,7 +92,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{{"digitheader"}, o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe}, @@ -105,17 +104,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_TOF", "TOF", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TOF", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TOF", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "tof-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 tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx index 3fc47955f53c0..27d7c162cf670 100644 --- a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace tof { - -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,13 +70,16 @@ 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("compDigits", o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe); inputs.emplace_back("patterns", o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); inputs.emplace_back("ROframes", o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -86,14 +88,12 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{o2::header::gDataOriginTOF, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TOF", "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"}}, {"irframe-shift", VariantType::Int, o2::tof::Geo::LATENCYWINDOW_IN_BC, {"IRFrame shift to account for latency"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx index 1969672ad3fa3..5cf882e2723d6 100644 --- a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TOF/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, "ccdb", {"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::tof::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::tof::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/TPC/CMakeLists.txt b/Detectors/TPC/CMakeLists.txt index b602e61e49fe2..aea0dee361874 100644 --- a/Detectors/TPC/CMakeLists.txt +++ b/Detectors/TPC/CMakeLists.txt @@ -10,9 +10,11 @@ # or submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(baserecsim) add_subdirectory(reconstruction) add_subdirectory(calibration) add_subdirectory(simulation) +add_subdirectory(simworkflow) add_subdirectory(monitor) add_subdirectory(workflow) add_subdirectory(qc) diff --git a/Detectors/TPC/base/CMakeLists.txt b/Detectors/TPC/base/CMakeLists.txt index d4c1bc4602d54..6456207e50530 100644 --- a/Detectors/TPC/base/CMakeLists.txt +++ b/Detectors/TPC/base/CMakeLists.txt @@ -12,7 +12,6 @@ o2_add_library(TPCBase SOURCES src/CalArray.cxx src/CalDet.cxx - src/CDBInterface.cxx src/ContainerFactory.cxx src/CRU.cxx src/DigitPos.cxx @@ -24,7 +23,6 @@ o2_add_library(TPCBase src/PadRegionInfo.cxx src/PadROCPos.cxx src/PadSecPos.cxx - src/Painter.cxx src/ParameterDetector.cxx src/ParameterElectronics.cxx src/ParameterGas.cxx @@ -37,7 +35,6 @@ o2_add_library(TPCBase src/CRUCalibHelpers.cxx src/IonTailSettings.cxx src/FEEConfig.cxx - src/DeadChannelMapCreator.cxx src/CommonModeCorrection.cxx PUBLIC_LINK_LIBRARIES Vc::Vc Boost::boost O2::DataFormatsTPC O2::DetectorsRaw O2::CCDB FairRoot::Base) @@ -45,7 +42,6 @@ o2_add_library(TPCBase o2_target_root_dictionary(TPCBase HEADERS include/TPCBase/CalArray.h include/TPCBase/CalDet.h - include/TPCBase/CDBInterface.h include/TPCBase/ContainerFactory.h include/TPCBase/CRU.h include/TPCBase/DigitPos.h @@ -57,7 +53,6 @@ o2_target_root_dictionary(TPCBase include/TPCBase/PadRegionInfo.h include/TPCBase/PadROCPos.h include/TPCBase/PadSecPos.h - include/TPCBase/Painter.h include/TPCBase/ParameterDetector.h include/TPCBase/ParameterElectronics.h include/TPCBase/ParameterGas.h @@ -70,22 +65,13 @@ o2_target_root_dictionary(TPCBase include/TPCBase/CRUCalibHelpers.h include/TPCBase/IonTailSettings.h include/TPCBase/FEEConfig.h - include/TPCBase/DeadChannelMapCreator.h - include/TPCBase/CommonModeCorrection.h - include/TPCBase/CDBTypes.h) + include/TPCBase/CommonModeCorrection.h) o2_add_test(Base COMPONENT_NAME tpc PUBLIC_LINK_LIBRARIES O2::TPCBase SOURCES test/testTPCBase.cxx LABELS tpc) -o2_add_test(CalDet - COMPONENT_NAME tpc - PUBLIC_LINK_LIBRARIES O2::TPCBase - SOURCES test/testTPCCalDet.cxx - ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage - LABELS tpc) - o2_add_test(Mapper COMPONENT_NAME tpc PUBLIC_LINK_LIBRARIES O2::TPCBase diff --git a/Detectors/TPC/base/include/TPCBase/CalArray.h b/Detectors/TPC/base/include/TPCBase/CalArray.h index 2679eae4e706e..c0d5a14bd86de 100644 --- a/Detectors/TPC/base/include/TPCBase/CalArray.h +++ b/Detectors/TPC/base/include/TPCBase/CalArray.h @@ -26,6 +26,11 @@ #include #endif +#ifdef NDEBUG +#undef NDEBUG +#include +#endif + namespace o2 { namespace tpc @@ -93,7 +98,11 @@ class CalArray int getPadSubsetNumber() const { return mPadSubsetNumber; } void setValue(const size_t channel, const T& value) { mData[channel] = value; } - const T getValue(const size_t channel) const { return mData[channel]; } + const T getValue(const size_t channel) const + { + assert(channel < mData.size()); + return mData[channel]; + } void setValue(const size_t row, const size_t pad, const T& value); const T getValue(const size_t row, const size_t pad) const; diff --git a/Detectors/TPC/base/include/TPCBase/CalDet.h b/Detectors/TPC/base/include/TPCBase/CalDet.h index cab1bd5757f27..76bbeaf8bebd1 100644 --- a/Detectors/TPC/base/include/TPCBase/CalDet.h +++ b/Detectors/TPC/base/include/TPCBase/CalDet.h @@ -30,6 +30,12 @@ #include "Rtypes.h" #endif +#ifndef NDEBUG +#undef NDEBUG +// always enable assert +#include +#endif + namespace o2 { namespace tpc @@ -211,7 +217,26 @@ inline const T CalDet::getValue(const ROC roc, const size_t row, const size_t } case PadSubset::Region: { const auto globalRow = roc.isOROC() ? mappedRow + mapper.getNumberOfRowsROC(ROC(0)) : mappedRow; - return mData[Mapper::REGION[globalRow] + roc.getSector() * Mapper::NREGIONS].getValue(Mapper::OFFSETCRUGLOBAL[globalRow] + mappedPad); + const auto dataRow = Mapper::REGION[globalRow] + roc.getSector() * Mapper::NREGIONS; + const auto index = Mapper::OFFSETCRUGLOBAL[globalRow] + mappedPad; + assert(dataRow < mData.size()); + if (index >= mData[dataRow].getData().size()) { + // S. Wenzel: We shouldn't come here but we do. For instance for CalDet calibrations loaded from + // creator.loadIDCPadFlags(1731274461770); + + // In this case there is an index overflow, leading to invalid reads and potentially a segfault. + // To increase stability, for now returning a trivial answer. This can be removed once either the algorithm + // or the calibration data has been fixed. +#ifndef GPUCA_ALIGPUCODE // hide from GPU standalone compilation + static bool printMsg = true; + if (printMsg) { + LOG(error) << "Out of bound access in TPC CalDet ROC " << roc << " row " << row << " pad " << pad << " (no more messages printed)"; + } + printMsg = false; +#endif + return T{}; + } + return mData[dataRow].getValue(index); break; } } diff --git a/Detectors/TPC/base/include/TPCBase/ParameterGEM.h b/Detectors/TPC/base/include/TPCBase/ParameterGEM.h index 2d55a550764ac..cb458fbb5dafa 100644 --- a/Detectors/TPC/base/include/TPCBase/ParameterGEM.h +++ b/Detectors/TPC/base/include/TPCBase/ParameterGEM.h @@ -54,6 +54,7 @@ struct ParameterGEM : public o2::conf::ConfigurableParamHelper { float AbsoluteGain[4] = {14.f, 8.f, 53.f, 240.f}; ///< Absolute gain float CollectionEfficiency[4] = {1.f, 0.2f, 0.25f, 1.f}; ///< Collection efficiency float ExtractionEfficiency[4] = {0.65f, 0.55f, 0.12f, 0.6f}; ///< Extraction efficiency + float RelativeGainStack[4] = {1.f, 1.f, 1.f, 1.f}; ///< Relative gain of the stack per region (IROC, OROC1, OROC2, OROC3) for the EffectiveMode float TotalGainStack = 2000.f; ///< Total gain of the stack for the EffectiveMode float KappaStack = 1.205f; ///< Variable steering the energy resolution of the full stack for the EffectiveMode float EfficiencyStack = 0.528f; ///< Variable steering the single electron efficiency of the full stack for the EffectiveMode diff --git a/Detectors/TPC/base/src/TPCBaseLinkDef.h b/Detectors/TPC/base/src/TPCBaseLinkDef.h index 60924db3953e2..2b7a7ff19542d 100644 --- a/Detectors/TPC/base/src/TPCBaseLinkDef.h +++ b/Detectors/TPC/base/src/TPCBaseLinkDef.h @@ -27,13 +27,9 @@ #pragma link C++ class o2::tpc::CalDet < unsigned> + ; #pragma link C++ class o2::tpc::CalDet < short> + ; #pragma link C++ class o2::tpc::CalDet < bool> + ; -#pragma link C++ class o2::tpc::CalDet < o2::tpc::PadFlags> + ; #pragma link C++ class std::vector < o2::tpc::CalDet < float>> + ; #pragma link C++ class std::vector < o2::tpc::CalDet < float>*> + ; #pragma link C++ class std::unordered_map < std::string, o2::tpc::CalDet < float>> + ; -#pragma link C++ enum o2::tpc::CDBType; -#pragma link C++ class o2::tpc::CDBInterface; -#pragma link C++ class o2::tpc::CDBStorage; #pragma link C++ class o2::tpc::ContainerFactory; #pragma link C++ class o2::tpc::CRU; #pragma link C++ class o2::tpc::DigitPos; @@ -49,8 +45,6 @@ #pragma link C++ class o2::tpc::ROC; #pragma link C++ class o2::tpc::Sector; -#pragma link C++ class o2::tpc::painter + ; - // #pragma link C++ class std::vector + ; #pragma link C++ class o2::tpc::ParameterDetector; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::ParameterDetector> + ; @@ -89,5 +83,4 @@ #pragma link C++ function o2::tpc::cru_calib_helpers::getCalPad < 2>(const std::string_view, const std::string_view, std::string_view) #pragma link C++ function o2::tpc::cru_calib_helpers::getCalPad < 6>(const std::string_view, const std::string_view, std::string_view) -#pragma link C++ class o2::tpc::DeadChannelMapCreator + ; #endif diff --git a/Detectors/TPC/base/test/testTPCCDBInterface.cxx b/Detectors/TPC/base/test/testTPCCDBInterface.cxx index 3074c5e90a00c..a0f4142b3f807 100644 --- a/Detectors/TPC/base/test/testTPCCDBInterface.cxx +++ b/Detectors/TPC/base/test/testTPCCDBInterface.cxx @@ -21,8 +21,7 @@ #include "TFile.h" // o2 includes -#include "TPCBase/CDBInterface.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/CalArray.h" #include "TPCBase/CalDet.h" #include "TPCBase/Mapper.h" diff --git a/Detectors/TPC/baserecsim/CMakeLists.txt b/Detectors/TPC/baserecsim/CMakeLists.txt new file mode 100644 index 0000000000000..b6c0f2644aa81 --- /dev/null +++ b/Detectors/TPC/baserecsim/CMakeLists.txt @@ -0,0 +1,35 @@ +# 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. + +o2_add_library(TPCBaseRecSim + SOURCES src/DeadChannelMapCreator.cxx + src/Painter.cxx + src/CDBInterface.cxx + PUBLIC_LINK_LIBRARIES O2::TPCBase) + +o2_target_root_dictionary(TPCBaseRecSim + EXTRA_PATCH src/TPCFlagsMemberCustomStreamer.cxx + HEADERS include/TPCBaseRecSim/Painter.h + include/TPCBaseRecSim/PadFlags.h + include/TPCBaseRecSim/DeadChannelMapCreator.h + include/TPCBaseRecSim/CDBTypes.h + include/TPCBaseRecSim/CDBInterface.h) + +if(BUILD_SIMULATION) + # this test needs CCDB/XROOTD which is for sure + # available in the default-o2 software stack + o2_add_test(CalDet + COMPONENT_NAME tpc + PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim + SOURCES test/testTPCCalDet.cxx + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage + LABELS tpc) +endif() diff --git a/Detectors/TPC/base/include/TPCBase/CDBInterface.h b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/CDBInterface.h similarity index 99% rename from Detectors/TPC/base/include/TPCBase/CDBInterface.h rename to Detectors/TPC/baserecsim/include/TPCBaseRecSim/CDBInterface.h index 4c28744f0378a..5b2c8e6d48251 100644 --- a/Detectors/TPC/base/include/TPCBase/CDBInterface.h +++ b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/CDBInterface.h @@ -25,8 +25,8 @@ #include "CCDB/CcdbApi.h" #include "TPCBase/CalDet.h" #include "TPCBase/FEEConfig.h" -#include "TPCBase/CDBTypes.h" -#include "TPCBase/DeadChannelMapCreator.h" +#include "TPCBaseRecSim/CDBTypes.h" +#include "TPCBaseRecSim/DeadChannelMapCreator.h" #include "DataFormatsTPC/LtrCalibData.h" #include "DataFormatsTPC/Defs.h" #include "CommonUtils/NameConf.h" diff --git a/Detectors/TPC/base/include/TPCBase/CDBTypes.h b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/CDBTypes.h similarity index 100% rename from Detectors/TPC/base/include/TPCBase/CDBTypes.h rename to Detectors/TPC/baserecsim/include/TPCBaseRecSim/CDBTypes.h diff --git a/Detectors/TPC/base/include/TPCBase/DeadChannelMapCreator.h b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/DeadChannelMapCreator.h similarity index 98% rename from Detectors/TPC/base/include/TPCBase/DeadChannelMapCreator.h rename to Detectors/TPC/baserecsim/include/TPCBaseRecSim/DeadChannelMapCreator.h index 9d4317380f4bc..5a3fc38aa208b 100644 --- a/Detectors/TPC/base/include/TPCBase/DeadChannelMapCreator.h +++ b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/DeadChannelMapCreator.h @@ -21,8 +21,8 @@ #include "CCDB/CcdbApi.h" -#include "DataFormatsTPC/Defs.h" -#include "TPCBase/CDBTypes.h" +#include "TPCBaseRecSim/PadFlags.h" +#include "TPCBaseRecSim/CDBTypes.h" #include "TPCBase/CalDet.h" #include "TPCBase/FEEConfig.h" diff --git a/Detectors/TPC/baserecsim/include/TPCBaseRecSim/PadFlags.h b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/PadFlags.h new file mode 100644 index 0000000000000..e13a24adf407e --- /dev/null +++ b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/PadFlags.h @@ -0,0 +1,44 @@ +// 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 Defs.h +/// @author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de +/// + +/// @brief Global TPC definitions and constants + +#ifndef AliceO2_TPC_PadFlags_H +#define AliceO2_TPC_PadFlags_H + +namespace o2::tpc +{ + +enum class PadFlags : unsigned short { + flagGoodPad = 1 << 0, ///< flag for a good pad binary 0001 + flagDeadPad = 1 << 1, ///< flag for a dead pad binary 0010 + flagUnknownPad = 1 << 2, ///< flag for unknown status binary 0100 + flagSaturatedPad = 1 << 3, ///< flag for saturated status binary 0100 + flagHighPad = 1 << 4, ///< flag for pad with extremly high IDC value + flagLowPad = 1 << 5, ///< flag for pad with extremly low IDC value + flagSkip = 1 << 6, ///< flag for defining a pad which is just ignored during the calculation of I1 and IDCDelta + flagFEC = 1 << 7, ///< flag for a whole masked FEC + flagNeighbour = 1 << 8, ///< flag if n neighbouring pads are outlier + flagAllNoneGood = flagDeadPad | flagUnknownPad | flagSaturatedPad | flagHighPad | flagLowPad | flagSkip | flagFEC | flagNeighbour, +}; + +inline PadFlags operator&(PadFlags a, PadFlags b) { return static_cast(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)); } + +} // namespace o2::tpc + +#endif diff --git a/Detectors/TPC/base/include/TPCBase/Painter.h b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/Painter.h similarity index 97% rename from Detectors/TPC/base/include/TPCBase/Painter.h rename to Detectors/TPC/baserecsim/include/TPCBaseRecSim/Painter.h index 976fe2846ce0c..5cf8691635b1f 100644 --- a/Detectors/TPC/base/include/TPCBase/Painter.h +++ b/Detectors/TPC/baserecsim/include/TPCBaseRecSim/Painter.h @@ -53,7 +53,8 @@ struct painter { enum class Type : int { Pad, ///< drawing pads Stack, ///< drawing stacks - FEC ///< drawing of FECs + FEC, ///< drawing of FECs + SCD, ///< drawing of FECs }; static std::array colors; @@ -87,8 +88,10 @@ struct painter { /// create a vector of FEC corner coordinates for one full sector static std::vector getFECCoordinatesSector(); + static std::vector getSCDY2XCoordinatesSector(std::string binningStr); + /// \return returns coordinates for given type - static std::vector getCoordinates(const Type type); + static std::vector getCoordinates(const Type type, std::string binningStr = ""); /// binning vector with radial pad-row positions (in cm) /// \param roc roc number (0-35 IROC, 36-71 OROC, >=72 full sector) @@ -143,11 +146,11 @@ struct painter { /// \param yMin minimum y coordinate of the histogram /// \param yMax maximum y coordinate of the histogram /// \param type granularity of the histogram (per pad or per stack) - static TH2Poly* makeSectorHist(const std::string_view name = "hSector", const std::string_view title = "Sector;local #it{x} (cm);local #it{y} (cm)", const float xMin = 83.65f, const float xMax = 247.7f, const float yMin = -43.7f, const float yMax = 43.7f, const Type type = Type::Pad); + static TH2Poly* makeSectorHist(const std::string_view name = "hSector", const std::string_view title = "Sector;local #it{x} (cm);local #it{y} (cm)", const float xMin = 83.65f, const float xMax = 247.7f, const float yMin = -43.7f, const float yMax = 43.7f, const Type type = Type::Pad, std::string binningStr = ""); /// make a side-wise histogram with correct pad corners /// \param type granularity of the histogram (per pad or per stack) - static TH2Poly* makeSideHist(Side side, const Type type = Type::Pad); + static TH2Poly* makeSideHist(Side side, const Type type = Type::Pad, std::string binningStr = ""); /// fill existing TH2Poly histogram for CalDet object /// \param h2D histogram to fill diff --git a/Detectors/TPC/base/src/CDBInterface.cxx b/Detectors/TPC/baserecsim/src/CDBInterface.cxx similarity index 99% rename from Detectors/TPC/base/src/CDBInterface.cxx rename to Detectors/TPC/baserecsim/src/CDBInterface.cxx index 605413b205c2a..2aaf9c58cbe2c 100644 --- a/Detectors/TPC/base/src/CDBInterface.cxx +++ b/Detectors/TPC/baserecsim/src/CDBInterface.cxx @@ -28,7 +28,7 @@ // o2 includes #include "DataFormatsTPC/CalibdEdxCorrection.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/ParameterDetector.h" #include "TPCBase/ParameterElectronics.h" #include "TPCBase/ParameterGEM.h" @@ -722,6 +722,8 @@ void CDBStorage::printObjectSummary(std::string_view name, CDBType const type, M { std::time_t tstart(start / 1000); std::time_t tend(end / 1000); + std::tm ttstart = *std::localtime(&tstart); + std::tm ttend = *std::localtime(&tend); auto tstartms = start % 1000; auto tendms = end % 1000; @@ -729,7 +731,7 @@ void CDBStorage::printObjectSummary(std::string_view name, CDBType const type, M fmt::format(" to storage '{}'\n", mCCDB.getURL()) + fmt::format(" into path '{}'\n", CDBTypeMap.at(type)) + fmt::format(" with validity [{}, {}] :", start, end) + - fmt::format(" [{:%d.%m.%Y %H:%M:%S}.{:03d}, {:%d.%m.%Y %H:%M:%S}.{:03d}]\n", fmt::localtime(tstart), tstartms, fmt::localtime(tend), tendms) + + fmt::format(" [{:%d.%m.%Y %H:%M:%S}.{:03d}, {:%d.%m.%Y %H:%M:%S}.{:03d}]\n", ttstart, tstartms, ttend, tendms) + std::string(" Meta data:\n"); for (const auto& [key, value] : metadata) { diff --git a/Detectors/TPC/base/src/DeadChannelMapCreator.cxx b/Detectors/TPC/baserecsim/src/DeadChannelMapCreator.cxx similarity index 98% rename from Detectors/TPC/base/src/DeadChannelMapCreator.cxx rename to Detectors/TPC/baserecsim/src/DeadChannelMapCreator.cxx index 8c4e754fc5327..2d41e277b8583 100644 --- a/Detectors/TPC/base/src/DeadChannelMapCreator.cxx +++ b/Detectors/TPC/baserecsim/src/DeadChannelMapCreator.cxx @@ -14,8 +14,8 @@ #include #include "CommonUtils/NameConf.h" #include "Framework/Logger.h" -#include "TPCBase/DeadChannelMapCreator.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/DeadChannelMapCreator.h" +#include "TPCBaseRecSim/Painter.h" using namespace o2::tpc; diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/baserecsim/src/Painter.cxx similarity index 92% rename from Detectors/TPC/base/src/Painter.cxx rename to Detectors/TPC/baserecsim/src/Painter.cxx index 9f143d3fa45ce..a571b50607dd2 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/baserecsim/src/Painter.cxx @@ -31,7 +31,9 @@ #include "TPaveText.h" #include "TPaletteAxis.h" #include "TObjArray.h" +#include "TMath.h" +#include "Algorithm/RangeTokenizer.h" #include "CommonUtils/StringUtils.h" #include "DataFormatsTPC/Defs.h" #include "TPCBase/ROC.h" @@ -39,7 +41,8 @@ #include "TPCBase/Mapper.h" #include "TPCBase/CalDet.h" #include "TPCBase/CalArray.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" +#include "TPCBaseRecSim/PadFlags.h" #include "TPCBase/Utils.h" #include "DataFormatsTPC/LaserTrack.h" @@ -223,7 +226,77 @@ std::vector painter::getFECCoordinatesSector() return padCoords; } -std::vector painter::getCoordinates(const Type type) +std::vector painter::getSCDY2XCoordinatesSector(std::string binningStr) +{ + const float deadZone = 1.5; + const float secPhi = 20.0 * TMath::DegToRad(); + std::vector padCoords; + const Mapper& mapper = Mapper::instance(); + const auto nPadRows = Mapper::PADROWS; + std::vector maxY2X(nPadRows); + auto binCenters = o2::RangeTokenizer::tokenize(binningStr); + size_t nY2XBins = 20; + std::vector halfBinWidth; + + auto setUniformBinning = [&binCenters, &halfBinWidth](int nY2XBins) { + binCenters.resize(nY2XBins); + halfBinWidth.resize(nY2XBins); + for (int i = 0; i < nY2XBins; ++i) { + const auto binWidth = 2.f / nY2XBins; + halfBinWidth[i] = binWidth / 2.f; + binCenters[i] = -1.f + (i + 0.5f) * binWidth; + } + }; + + if (binCenters.size() == 0) { + LOGP(info, "Empty binning provided, will use default uniform y/x binning with {} bins", nY2XBins); + setUniformBinning(nY2XBins); + } else if (binCenters.size() == 1) { + nY2XBins = static_cast(binCenters.at(0)); + LOGP(info, "Setting uniform binning for y/x with {} bins", nY2XBins); + setUniformBinning(nY2XBins); + } else { + nY2XBins = binCenters.size() - 1; + if (std::abs(binCenters[0] + 1.f) > 1e-6 || std::abs(binCenters[nY2XBins] - 1.f) > 1e-6) { + LOG(error) << "Provided binning for y/x not in range -1 to 1: " << binCenters[0] << " - " << binCenters[nY2XBins] << ". Using default uniform binning with " << nY2XBins << " bins"; + setUniformBinning(nY2XBins); + } else { + LOGP(info, "Setting custom binning for y/x with {} bins", nY2XBins); + halfBinWidth.reserve(nY2XBins); + halfBinWidth.clear(); + for (int i = 0; i < nY2XBins; ++i) { + halfBinWidth.push_back(.5f * (binCenters[i + 1] - binCenters[i])); + binCenters[i] = .5f * (binCenters[i] + binCenters[i + 1]); + } + binCenters.resize(nY2XBins); + } + } + + for (int irow = 0; irow < nPadRows; ++irow) { + const auto x = mapper.getPadCentre(PadPos(irow, 0)).X(); + maxY2X[irow] = std::tan(.5f * secPhi) - deadZone / x; + const auto region = Mapper::REGION[irow]; + const auto ph = mapper.getPadRegionInfo(region).getPadHeight(); + const auto xPadBottom = x - ph / 2; + const auto xPadTop = x + ph / 2; + for (int iy2x = 0; iy2x < nY2XBins; ++iy2x) { + auto& padCoord = padCoords.emplace_back(); + float yPadRight = 0; + if (iy2x == 0) { + yPadRight = maxY2X[irow] * (binCenters[iy2x] - halfBinWidth[iy2x]); + } else { + yPadRight = maxY2X[irow] * (binCenters[iy2x - 1] + halfBinWidth[iy2x - 1]); + } + const auto yPadLeft = maxY2X[irow] * (binCenters[iy2x] + halfBinWidth[iy2x]); + padCoord.xVals = {xPadBottom, xPadTop, xPadTop, xPadBottom}; + padCoord.yVals = {yPadRight * xPadBottom, yPadRight * xPadTop, yPadLeft * xPadTop, yPadLeft * xPadBottom}; + } + } + + return padCoords; +} + +std::vector painter::getCoordinates(const Type type, std::string binningStr) { if (type == Type::Pad) { return painter::getPadCoordinatesSector(); @@ -231,6 +304,8 @@ std::vector painter::getCoordinates(const Type return painter::getStackCoordinatesSector(); } else if (type == Type::FEC) { return painter::getFECCoordinatesSector(); + } else if (type == Type::SCD) { + return painter::getSCDY2XCoordinatesSector(binningStr); } else { LOGP(warning, "Wrong Type provided!"); return std::vector(); @@ -291,6 +366,8 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float const Mapper& mapper = Mapper::instance(); + const bool draw1D = nbins1D > 0; + // ===| name and title |====================================================== std::string title = calDet.getName(); std::string name = calDet.getName(); @@ -305,11 +382,13 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float const int bufferSize = TH1::GetDefaultBufferSize(); TH1::SetDefaultBufferSize(Sector::MAXSECTOR * mapper.getPadsInSector()); - auto hAside1D = new TH1F(fmt::format("h_Aside_1D_{}", name).data(), fmt::format("{0} (A-Side);{0}", title).data(), - nbins1D, xMin1D, xMax1D); // TODO: modify ranges + auto hAside1D = draw1D ? new TH1F(fmt::format("h_Aside_1D_{}", name).data(), fmt::format("{0} (A-Side);{0}", title).data(), + nbins1D, xMin1D, xMax1D) + : nullptr; // TODO: modify ranges - auto hCside1D = new TH1F(fmt::format("h_Cside_1D_{}", name).data(), fmt::format("{0} (C-Side);{0}", title).data(), - nbins1D, xMin1D, xMax1D); // TODO: modify ranges + auto hCside1D = draw1D ? new TH1F(fmt::format("h_Cside_1D_{}", name).data(), fmt::format("{0} (C-Side);{0}", title).data(), + nbins1D, xMin1D, xMax1D) + : nullptr; // TODO: modify ranges auto hAside2D = new TH2F(fmt::format("h_Aside_2D_{}", name).data(), fmt::format("{0} (A-Side);#it{{x}} (cm);#it{{y}} (cm);{0}", title).data(), 330, -270, 270, 330, -270, 270); @@ -336,7 +415,9 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float if (!hist2D->GetBinContent(bin)) { hist2D->SetBinContent(bin, double(val)); } - hist1D->Fill(double(val)); + if (draw1D) { + hist1D->Fill(double(val)); + } } } } @@ -352,13 +433,13 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float gStyle->SetOptStat("mr"); auto c = outputCanvas; if (!c) { - c = new TCanvas(fmt::format("c_{}", name).data(), title.data(), 1000, 1000); + c = new TCanvas(fmt::format("c_{}", name).data(), title.data(), 1000, draw1D ? 1000 : 500); } gStyle->SetStatX(1. - gPad->GetRightMargin()); gStyle->SetStatY(1. - gPad->GetTopMargin()); c->Clear(); - c->Divide(2, 2); + c->Divide(2, draw1D ? 2 : 1); c->cd(1); hAside2D->Draw("colz"); @@ -376,18 +457,22 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float adjustPalette(hCside2D, 0.92); drawSectorsXY(Side::C); - c->cd(3); - hAside1D->Draw(); + if (draw1D) { + c->cd(3); + hAside1D->Draw(); - c->cd(4); - hCside1D->Draw(); + c->cd(4); + hCside1D->Draw(); + + // associate histograms to canvas + hAside1D->SetBit(TObject::kCanDelete); + hCside1D->SetBit(TObject::kCanDelete); + } // reset the buffer size TH1::SetDefaultBufferSize(bufferSize); // associate histograms to canvas - hAside1D->SetBit(TObject::kCanDelete); - hCside1D->SetBit(TObject::kCanDelete); hAside2D->SetBit(TObject::kCanDelete); hCside2D->SetBit(TObject::kCanDelete); @@ -795,11 +880,11 @@ std::vector painter::makeSummaryCanvases(const std::string_view fileNa } //______________________________________________________________________________ -TH2Poly* painter::makeSectorHist(const std::string_view name, const std::string_view title, const float xMin, const float xMax, const float yMin, const float yMax, const Type type) +TH2Poly* painter::makeSectorHist(const std::string_view name, const std::string_view title, const float xMin, const float xMax, const float yMin, const float yMax, const Type type, std::string binningStr) { auto poly = new TH2Poly(name.data(), title.data(), xMin, xMax, yMin, yMax); - auto coords = painter::getCoordinates(type); + auto coords = painter::getCoordinates(type, binningStr); for (const auto& coord : coords) { poly->AddBin(coord.xVals.size(), coord.xVals.data(), coord.yVals.data()); } @@ -808,12 +893,12 @@ TH2Poly* painter::makeSectorHist(const std::string_view name, const std::string_ } //______________________________________________________________________________ -TH2Poly* painter::makeSideHist(Side side, const Type type) +TH2Poly* painter::makeSideHist(Side side, const Type type, std::string binningStr) { const auto s = (side == Side::A) ? "A" : "C"; auto poly = new TH2Poly(fmt::format("hSide_{}", s).data(), fmt::format("{}-Side;#it{{x}} (cm);#it{{y}} (cm)", s).data(), -270., 270., -270., 270.); - auto coords = painter::getCoordinates(type); + auto coords = painter::getCoordinates(type, binningStr); for (int isec = 0; isec < 18; ++isec) { const float angDeg = 10.f + isec * 20; for (auto coord : coords) { diff --git a/Detectors/TPC/baserecsim/src/TPCBaseRecSimLinkDef.h b/Detectors/TPC/baserecsim/src/TPCBaseRecSimLinkDef.h new file mode 100644 index 0000000000000..37822e3c02669 --- /dev/null +++ b/Detectors/TPC/baserecsim/src/TPCBaseRecSimLinkDef.h @@ -0,0 +1,27 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ enum o2::tpc::PadFlags + ; // enum itself +#pragma link C++ class std::vector < o2::tpc::PadFlags> + ; +#pragma link C++ enum o2::tpc::CDBType; +#pragma link C++ class o2::tpc::CDBInterface; +#pragma link C++ class o2::tpc::CDBStorage; +#pragma link C++ class o2::tpc::CalArray < o2::tpc::PadFlags> + ; +#pragma link C++ class o2::tpc::CalDet < o2::tpc::PadFlags> + ; +#pragma link C++ class o2::tpc::painter + ; +#pragma link C++ class o2::tpc::DeadChannelMapCreator + ; +#endif diff --git a/Detectors/TPC/baserecsim/src/TPCFlagsMemberCustomStreamer.cxx b/Detectors/TPC/baserecsim/src/TPCFlagsMemberCustomStreamer.cxx new file mode 100644 index 0000000000000..27ebfeb3c64bb --- /dev/null +++ b/Detectors/TPC/baserecsim/src/TPCFlagsMemberCustomStreamer.cxx @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TPCBase/CalArray.h" +#include +#include +#include +#include + +// to enable assert statements +#ifdef NDEBUG +#undef NDEBUG +#include +#endif + +// The following code provides a specific ROOT I/O streaming method +// for the mData member of CalArray +// This member was written incorrectly to the TFile in ROOT versions < 6.36, causing +// segfaults when reading on ARM64 (occassionally). +// We continue to write it in the incorrect format and fix the reading back. + +// See also: +// - https://github.com/root-project/root/pull/17009 +// - https://its.cern.ch/jira/browse/O2-4671 + +void MemberVectorPadFlagsStreamer(TBuffer& R__b, void* objp, int n) +{ + if (n != 1) { + std::cerr << "Error in MemberVectorPadFlagsStreamer : Unexpected n " << n << std::endl; + return; + } + std::vector* obj = static_cast*>(objp); + if (R__b.IsReading()) { + obj->clear(); + std::vector R__stl; + R__stl.clear(); + int R__n; + R__b >> R__n; + R__stl.reserve(R__n); + for (int R__i = 0; R__i < R__n; R__i++) { + Int_t readtemp; + R__b >> readtemp; + R__stl.push_back(readtemp); + } + auto data = reinterpret_cast(R__stl.data()); + constexpr size_t bloatfactor = sizeof(int) / sizeof(o2::tpc::PadFlags); + for (int i = 0; i < bloatfactor * R__n; ++i) { + obj->push_back(static_cast(data[i])); + } + } else { + // We always save things with the old format. + R__b << (int)obj->size() / 2; + for (size_t i = 0; i < obj->size(); i++) { + R__b << (short)obj->at(i); + } + } +} + +// register the streamer via static global initialization (on library load) +// the streamer is only correct in combination with new ROOT +#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 33, 00) +namespace ROOT +{ +static __attribute__((used)) int _R__dummyStreamer_3 = + ([]() { + if (!getenv("TPC_PADFLAGS_STREAMER_OFF")) { + ROOT::GenerateInitInstance((o2::tpc::CalArray *)nullptr)->AdoptMemberStreamer("mData", new TMemberStreamer(MemberVectorPadFlagsStreamer)); + } + return 0; + })(); +} // namespace ROOT +#endif diff --git a/Detectors/TPC/base/test/testTPCCalDet.cxx b/Detectors/TPC/baserecsim/test/testTPCCalDet.cxx similarity index 97% rename from Detectors/TPC/base/test/testTPCCalDet.cxx rename to Detectors/TPC/baserecsim/test/testTPCCalDet.cxx index b93e952084396..bf4cfddb780f0 100644 --- a/Detectors/TPC/base/test/testTPCCalDet.cxx +++ b/Detectors/TPC/baserecsim/test/testTPCCalDet.cxx @@ -24,6 +24,7 @@ #include "TPCBase/CalDet.h" #include "TFile.h" #include "Framework/TypeTraits.h" +#include "TPCBaseRecSim/DeadChannelMapCreator.h" namespace o2::tpc { @@ -344,4 +345,12 @@ BOOST_AUTO_TEST_CASE(CalDetTypeTest) BOOST_CHECK(testDict == true); } +BOOST_AUTO_TEST_CASE(CalDetStreamerTest) +{ + // simple code executing the TPC IDCPadFlags loading in a standalone env --> easy to valgrind + o2::tpc::DeadChannelMapCreator creator{}; + creator.init("https://alice-ccdb.cern.ch"); + creator.loadIDCPadFlags(1731274461770); +} + } // namespace o2::tpc diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 8bcb3254edb32..27f7f0200bb92 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -25,7 +25,6 @@ o2_add_library(TPCCalibration src/CalibPadGainTracksBase.cxx src/CalibLaserTracks.cxx src/LaserTracksCalibrator.cxx - src/NeuralNetworkClusterizer.cxx src/SACDecoder.cxx src/IDCAverageGroup.cxx src/IDCAverageGroupBase.cxx @@ -59,7 +58,7 @@ o2_add_library(TPCCalibration src/DigitAdd.cxx src/CorrectdEdxDistortions.cxx src/PressureTemperatureHelper.cxx - PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBase + PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim O2::TPCReconstruction ROOT::Minuit Microsoft.GSL::GSL O2::DetectorsCalibration @@ -84,7 +83,6 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/FastHisto.h include/TPCCalibration/CalibLaserTracks.h include/TPCCalibration/LaserTracksCalibrator.h - include/TPCCalibration/NeuralNetworkClusterizer.h include/TPCCalibration/SACDecoder.h include/TPCCalibration/IDCAverageGroup.h include/TPCCalibration/IDCAverageGroupBase.h @@ -120,16 +118,16 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/PressureTemperatureHelper.h) o2_add_test_root_macro(macro/comparePedestalsAndNoise.C - PUBLIC_LINK_LIBRARIES O2::TPCBase + PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim LABELS tpc) o2_add_test_root_macro(macro/drawNoiseAndPedestal.C - PUBLIC_LINK_LIBRARIES O2::TPCBase + PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim LABELS tpc) o2_add_test_root_macro(macro/drawPulser.C - PUBLIC_LINK_LIBRARIES O2::TPCBase + PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim LABELS tpc) o2_add_test_root_macro(macro/mergeNoiseAndPedestal.C - PUBLIC_LINK_LIBRARIES O2::TPCBase + PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim LABELS tpc) o2_add_test_root_macro(macro/runPedestal.C PUBLIC_LINK_LIBRARIES O2::TPCCalibration diff --git a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt index 1b5e79f601211..47bb9c09a9951 100644 --- a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt +++ b/Detectors/TPC/calibration/SpacePoints/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(SpacePoints SOURCES src/SpacePointsCalibParam.cxx @@ -19,6 +20,7 @@ o2_add_library(SpacePoints O2::CommonUtils O2::TPCBase O2::TRDBase + O2::TOFBase O2::TPCReconstruction O2::TPCFastTransformation O2::ITStracking @@ -27,7 +29,8 @@ o2_add_library(SpacePoints O2::DataFormatsITSMFT O2::DataFormatsTRD O2::DataFormatsTOF - O2::DataFormatsGlobalTracking) + O2::DataFormatsGlobalTracking + O2::GPUTracking) o2_target_root_dictionary(SpacePoints HEADERS include/SpacePoints/TrackResiduals.h diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h index a02d830cfe45d..00af697da3a9b 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h @@ -49,7 +49,7 @@ struct ResidualsContainer { void fillStatisticsBranches(); uint64_t getNEntries() const { return nResidualsTotal; } - void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput); + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span detInfoRes, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput); void merge(ResidualsContainer* prev); void print(); void writeToFile(bool closeFileAfterwards); @@ -64,6 +64,7 @@ struct ResidualsContainer { std::vector sumUnbinnedResid, *sumUnbinnedResidPtr{&sumUnbinnedResid}; ///< sum of unbinned residuals for each TF std::vector lumi, *lumiPtr{&lumi}; ///< luminosity information from CTP per TF std::vector unbinnedRes, *unbinnedResPtr{&unbinnedRes}; ///< unbinned residuals which are sent to the aggregator + std::vector detInfoUnbRes, *detInfoUnbResPtr{&detInfoUnbRes}; ///< detector info associated to unbinned residuals which are sent to the aggregator std::vector trkData, *trkDataPtr{&trkData}; ///< track data and cluster ranges std::vector trackInfo, *trackInfoPtr{&trackInfo}; ///< allows to obtain track type for each unbinned residual downstream o2::ctp::LumiInfo lumiTF; ///< for each processed TF we store the lumi information in the tree of unbinned residuals diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h index 819ca7b0ae07f..8b884209dd697 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h @@ -41,6 +41,8 @@ struct SpacePointsCalibConfParam : public o2::conf::ConfigurableParamHelper(dyIn * 0x7fff / param::MaxResid)), - dz(static_cast(dzIn * 0x7fff / param::MaxResid)), - tgSlp(static_cast(tgSlpIn * 0x7fff / param::MaxTgSlp)), - y(static_cast(yIn * 0x7fff / param::MaxY)), - z(static_cast(zIn * 0x7fff / param::MaxZ)), - row(rowIn), - sec(secIn) {} - short dy; ///< residual in y - short dz; ///< residual in z - short tgSlp; ///< tan of the phi angle between padrow and track - short y; ///< y position of the track, needed for binning - short z; ///< z position of the track, needed for binning - unsigned char row; ///< TPC pad row - unsigned char sec; ///< TPC sector (0..35) - ClassDefNV(UnbinnedResid, 1); + UnbinnedResid(float dyIn, float dzIn, float tgSlpIn, float yIn, float zIn, unsigned char rowIn, unsigned char secIn, short chanIn = -1) : dy(static_cast(dyIn * 0x7fff / param::MaxResid)), + dz(static_cast(dzIn * 0x7fff / param::MaxResid)), + tgSlp(static_cast(tgSlpIn * 0x7fff / param::MaxTgSlp)), + y(static_cast(yIn * 0x7fff / param::MaxY)), + z(static_cast(zIn * 0x7fff / param::MaxZ)), + row(rowIn), + sec(secIn), + channel(chanIn) {} + short dy{0}; ///< residual in y + short dz{0}; ///< residual in z + short tgSlp{0}; ///< tan of the phi angle between padrow and track + short y{0}; ///< y position of the track, needed for binning + short z{0}; ///< z position of the track, needed for binning + unsigned char row{0}; ///< TPC pad row + unsigned char sec{0}; ///< TPC sector (0..35) + short channel{-1}; ///< extra channel info (ITS chip ID, TRD chamber, TOF main pad within the sector) + + bool isTPC() const { return row < constants::MAXGLOBALPADROW; } + bool isTRD() const { return row >= 160 && row < 166; } + bool isTOF() const { return row == 170; } + bool isITS() const { return row >= 180; } + int getDetID() const { return isTPC() ? 1 : (isITS() ? 0 : (isTRD() ? 2 : (isTOF() ? 3 : -1))); } + int getITSLayer() const { return row - 180; } + int getTRDLayer() const { return row - 170; } + float getAlpha() const; + float getX() const; + + static void init(long timestamp = -1); + static void checkInitDone(); + static bool gInitDone; + + ClassDefNV(UnbinnedResid, 2); +}; + +struct DetInfoResid { // detector info associated with residual + uint32_t word = 0; // container interpreted in a different way depending on the detector type + // + // TPC view: qTot and qMax of the cluster + uint16_t qTotTPC() const { return static_cast(word & 0xFFFFu); } + uint16_t qMaxTPC() const { return static_cast((word >> 16) & 0xFFFFu); } + void setTPC(uint16_t qTot, uint16_t qMax) { word = (static_cast(qMax) << 16) | static_cast(qTot); } + // + // TRD view: q0, q1, q2 + calibrated slope (truncated to in +-3.5 range) + static constexpr uint32_t TRDQ0NB = 7, TRDQ1NB = 7, TRDQ2NB = 6, TRDSlpNB = 12; + static constexpr uint32_t TRDQ0Msk = (1 << TRDQ0NB) - 1, TRDQ1Msk = (1 << TRDQ1NB) - 1, TRDQ2Msk = ((1 << TRDQ2NB) - 1), TRDSlpMsk = (1 << TRDSlpNB) - 1; + static constexpr float TRDMaxSlope = 3.5, TRDSlope2Int = ((1 << TRDSlpNB) - 1) / (2 * TRDMaxSlope), TRDInt2Slope = 1.f / TRDSlope2Int; + uint16_t q0TRD() const { return static_cast(word & TRDQ0Msk); } + uint16_t q1TRD() const { return static_cast((word >> TRDQ0NB) & TRDQ1Msk); } + uint16_t q2TRD() const { return static_cast((word >> (TRDQ0NB + TRDQ1NB)) & TRDQ2Msk); } + float slopeTRD() const { return ((word >> (TRDQ0NB + TRDQ1NB + TRDQ2NB)) & TRDSlpMsk) * TRDInt2Slope - TRDMaxSlope; } + void setTRD(uint8_t q0, uint8_t q1, uint8_t q2, float slope) + { + float rslope = (slope + TRDMaxSlope) * TRDSlope2Int; + if (rslope < 0.f) { + rslope = 0; + } else if (rslope > TRDSlpMsk) { + rslope = TRDSlpMsk; + } + uint32_t slpI = std::round(rslope); + word = (static_cast(slpI << (TRDQ0NB + TRDQ1NB + TRDQ2NB)) | + static_cast((q2 & TRDQ2Msk) << (TRDQ0NB + TRDQ1NB)) | + static_cast((q1 & TRDQ1Msk) << TRDQ0NB) | + static_cast(q0 & TRDQ0Msk)); + } + // + // TOF view (time difference in \mus wrt seeding ITS-TPC track) + float timeTOF() const { return std::bit_cast(word); } + void setTOF(float t) { word = std::bit_cast(t); } + // + // No info for ITS is stored + // + // PV view (time difference in \mus wrt contributing ITS-TPC track) + float timePV() const { return std::bit_cast(word); } + void setPV(float t) { word = std::bit_cast(t); } + + ClassDefNV(DetInfoResid, 1); }; /// Structure for the information required to associate each residual with a given track type (ITS-TPC-TRD-TOF, etc) struct TrackDataCompact { TrackDataCompact() = default; - TrackDataCompact(uint32_t idx, uint8_t nRes, uint8_t source) : idxFirstResidual(idx), nResiduals(nRes), sourceId(source) {} + TrackDataCompact(uint32_t idx, std::array mlt, uint8_t nRes, uint8_t source, uint8_t nextraRes = 0) : idxFirstResidual(idx), multStack{mlt}, nResiduals(nRes), sourceId(source), nExtDetResid(nextraRes) {} uint32_t idxFirstResidual; ///< the index of the first residual from this track - uint8_t nResiduals; ///< total number of residuals associated to this track + std::array multStack{}; // multiplicity in the stack packed as asinh(x*0.05)/0.05 + uint8_t nResiduals; ///< total number of TPC residuals associated to this track + uint8_t nExtDetResid = 0; ///< number of external detectors (wrt TPC) residuals stored, on top of clIdx.getEntries uint8_t sourceId; ///< source ID obtained from the global track ID - ClassDefNV(TrackDataCompact, 1); + + void setMultStack(float v, int stack) + { + uint32_t mltPacked = std::round(std::asinh(v * 0.05) / 0.05); + multStack[stack] = mltPacked < 0xff ? mltPacked : 0xff; + } + float getMultStack(int stack) const + { + return std::sinh(multStack[stack] * 0.05) / 0.05; + } + float getMultStackPacked(int stack) const { return multStack[stack]; } + + ClassDefNV(TrackDataCompact, 3); }; // TODO add to UnbinnedResid::sec flag if cluster was used or not @@ -110,7 +190,8 @@ struct TrackDataExtended { std::vector clsTRD{}; ///< the TRD space points (if available) o2::tof::Cluster clsTOF{}; ///< the TOF cluster (if available) o2::dataformats::RangeReference<> clIdx{}; ///< index of first cluster residual and total number of cluster residuals of this track - ClassDefNV(TrackDataExtended, 2); + uint8_t nExtDetResid = 0; ///< number of external detectors (to TPC) residuals stored, on top of clIdx.getEntries + ClassDefNV(TrackDataExtended, 3); }; /// Structure filled for each track with track quality information and a vector with TPCClusterResiduals @@ -121,12 +202,31 @@ struct TrackData { float chi2TPC{}; ///< chi2 of TPC track float chi2ITS{}; ///< chi2 of ITS track float chi2TRD{}; ///< chi2 of TRD track + float deltaTOF{}; ///< TOFsignal - T0 - texp(PID), if T0 is available + unsigned short nClsTPC{}; ///< number of attached TPC clusters unsigned short nClsITS{}; ///< number of attached ITS clusters unsigned short nTrkltsTRD{}; ///< number of attached TRD tracklets - unsigned short clAvailTOF{}; ///< whether or not track seed has a matched TOF cluster - o2::dataformats::RangeReference<> clIdx{}; ///< index of first cluster residual and total number of cluster residuals of this track - ClassDefNV(TrackData, 6); + unsigned short clAvailTOF{}; ///< whether or not track seed has a matched TOF cluster, if so, gives the resolution of the T0 in ps + short TRDTrkltSlope[6] = {}; ///< TRD tracklet slope 0x7fff / param::MaxTRDSlope + uint8_t nExtDetResid = 0; ///< number of external detectors (to TPC) residuals stored, on top of clIdx.getEntries + o2::dataformats::RangeReference<> clIdx{}; ///< index of first cluster residual and total number of TPC cluster residuals of this track + std::array multStack{}; // multiplicity in the stack packed as asinh(x*0.05)/0.05 + float getT0Error() const { return float(clAvailTOF); } + bool isTOFAvail() const { return clAvailTOF != 0; } + + void setMultStack(float v, int stack) + { + uint32_t mltPacked = std::round(std::asinh(v * 0.05) / 0.05); + multStack[stack] = mltPacked < 0xff ? mltPacked : 0xff; + } + float getMultStack(int stack) const + { + return std::sinh(multStack[stack] * 0.05) / 0.05; + } + float getMultStackPacked(int stack) const { return multStack[stack]; } + + ClassDefNV(TrackData, 10); }; /// \class TrackInterpolation @@ -212,6 +312,8 @@ class TrackInterpolation /// Reset cache and output vectors void reset(); + // refit ITS track taking PID (unless already refitted) from the seed and reassign to the seed + bool refITSTrack(o2::dataformats::GlobalTrackID, int iSeed); // -------------------------------------- outlier rejection -------------------------------------------------- /// Validates the given input track and its residuals @@ -239,6 +341,8 @@ class TrackInterpolation void diffToMA(const int np, const std::array& y, std::array& diffMA) const; // -------------------------------------- settings -------------------------------------------------- + void setNHBPerTF(int n) { mNHBPerTF = n; } + void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); /// Sets the flag if material correction should be applied when extrapolating the tracks @@ -265,8 +369,15 @@ class TrackInterpolation /// Set the centre of mass energy required for pT downsampling Tsalis function void setSqrtS(float s) { mSqrtS = s; } + void setExtDetResid(bool v) { mExtDetResid = v; } + + int processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLayer, o2::track::TrackParCov& trkWork, std::array* trkltTRDYZ = nullptr, + std::array* trkltTRDCov = nullptr, TrackData* trkData = nullptr, + o2::trd::Tracklet64* trk64 = nullptr, o2::trd::CalibratedTracklet* trkCalib = nullptr); + // --------------------------------- output --------------------------------------------- std::vector& getClusterResiduals() { return mClRes; } + std::vector& getClusterResidualsDetInfo() { return mDetInfoRes; } std::vector& getTrackDataCompact() { return mTrackDataCompact; } std::vector& getTrackDataExtended() { return mTrackDataExtended; } std::vector& getReferenceTracks() { return mTrackData; } @@ -275,8 +386,14 @@ class TrackInterpolation private: static constexpr float sFloatEps{1.e-7f}; ///< float epsilon for robust linear fitting + static constexpr int NSTACKS = 4; + static constexpr std::array STACKROWS{0, 63, 97, 127, 152}; // parameters + settings const SpacePointsCalibConfParam* mParams = nullptr; + std::shared_ptr mTPCParam = nullptr; + int mNHBPerTF = 32; + int mNTPCOccBinLength = 16; ///< TPC occupancy bin length in TB + float mNTPCOccBinLengthInv = 1.f / 16; ///< its inverse float mTPCTimeBinMUS{.2f}; ///< TPC time bin duration in us float mTPCVDriftRef = -1.; ///< TPC nominal drift speed in cm/microseconds float mTPCDriftTimeOffsetRef = 0.; ///< TPC nominal (e.g. at the start of run) drift time bias in cm/mus @@ -285,6 +402,7 @@ class TrackInterpolation int mMaxTracksPerTF{-1}; ///< max number of tracks to be processed per TF (-1 means there is no limit) int mAddTracksForMapPerTF{0}; ///< in case residuals from different track types are used for vDrift calibration and map creation this defines the statistics for the latter bool mDumpTrackPoints{false}; ///< dump also track points in ITS, TRD and TOF + bool mExtDetResid{true}; ///< produce unbinned residuals for external detectors bool mProcessSeeds{false}; ///< in case for global tracks also their shorter parts are processed separately bool mProcessITSTPConly{false}; ///< flag, whether or not to extrapolate ITS-only through TPC o2::dataformats::GlobalTrackID::mask_t mSourcesConfigured; ///< the track sources taken into account for extra-/interpolation @@ -296,7 +414,9 @@ class TrackInterpolation std::vector mGIDs{}; ///< GIDs of input tracks std::vector mGIDtables{}; ///< GIDs of contributors from single detectors for each seed std::vector mTrackTimes{}; ///< time estimates for all input tracks in micro seconds + std::vector mTrackPVID{}; ///< track vertex index (if any) std::vector mSeeds{}; ///< seeding track parameters (ITS tracks) + std::vector mParentID{}; ///< entry of more global parent track for skimmed seeds (-1: no parent) std::map mTrackTypes; ///< mapping of track source to array index in mTrackIndices std::array, 4> mTrackIndices; ///< keep GIDs of input tracks separately for each track type gsl::span mTPCTracksClusIdx; ///< input TPC cluster indices from span @@ -304,6 +424,7 @@ class TrackInterpolation // ITS specific input only needed for debugging gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters + std::vector mITSRefitSeedID; ///< seed ID first using refitted ITS track const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary // output @@ -311,6 +432,7 @@ class TrackInterpolation std::vector mTrackDataCompact{}; ///< required to connect each residual to a global track std::vector mTrackDataExtended{}; ///< full tracking information for debugging std::vector mClRes{}; ///< residuals for each available TPC cluster of all tracks + std::vector mDetInfoRes{}; ///< packed detector info associated with each residual std::vector mTrackDataUnfiltered{}; ///< same as mTrackData, but for all tracks before outlier filtering std::vector mClResUnfiltered{}; ///< same as mClRes, but for all residuals before outlier filtering @@ -319,7 +441,7 @@ class TrackInterpolation std::vector mGIDsSuccess; ///< keep track of the GIDs which could be processed successfully // helpers - o2::trd::RecoParam mRecoParam; ///< parameters required for TRD refit + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD refit o2::trd::Geometry* mGeoTRD; ///< TRD geometry instance (needed for tilted pad correction) std::unique_ptr mFastTransform{}; ///< TPC cluster transformation float mBz; ///< required for helix approximation diff --git a/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx b/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx index a120c0e4ae782..b916e14dbf741 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx @@ -124,6 +124,7 @@ void ResidualsContainer::init(const TrackResiduals* residualsEngine, std::string treeOutResidualsUnbinned->Branch("trackInfo", &trackInfoPtr); treeOutResidualsUnbinned->Branch("CTPLumi", &lumiTF); treeOutResidualsUnbinned->Branch("timeMS", &timeMS); + treeOutResidualsUnbinned->Branch("detInfo", &detInfoUnbResPtr); } if (writeTrackData) { treeOutTrackData = std::make_unique("trackData", "Track information incl cluster range ref"); @@ -170,7 +171,7 @@ void ResidualsContainer::fillStatisticsBranches() } } -void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput) +void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span detInfoRes, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput) { // receives large vector of unbinned residuals and fills the sector-wise vectors // with binned residuals and statistics @@ -185,13 +186,14 @@ void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::sp firstSeenTF = ti.tfCounter; } for (const auto& residIn : resid) { - ++nUnbinnedResidualsInTF; bool counterIncremented = false; if (writeUnbinnedResiduals) { unbinnedRes.push_back(residIn); + detInfoUnbRes.push_back(detInfoRes.size() ? detInfoRes[nUnbinnedResidualsInTF] : DetInfoResid{}); ++nResidualsTotal; counterIncremented = true; } + ++nUnbinnedResidualsInTF; if (!writeBinnedResid) { continue; } @@ -247,6 +249,7 @@ void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::sp timeMS = orbitReset + ti.tfCounter * o2::constants::lhc::LHCOrbitMUS * 1.e-3; treeOutResidualsUnbinned->Fill(); unbinnedRes.clear(); + detInfoUnbRes.clear(); trackInfo.clear(); } tfOrbits.push_back(ti.firstTForbit); @@ -338,6 +341,9 @@ void ResidualsContainer::merge(ResidualsContainer* prev) if (writeUnbinnedResiduals) { prev->treeOutResidualsUnbinned->SetBranchAddress("res", &unbinnedResPtr); prev->treeOutResidualsUnbinned->SetBranchAddress("trackInfo", &trackInfoPtr); + prev->treeOutResidualsUnbinned->SetBranchAddress("CTPLumi", &lumiTF); + prev->treeOutResidualsUnbinned->SetBranchAddress("timeMS", &timeMS); + prev->treeOutResidualsUnbinned->SetBranchAddress("detInfo", &detInfoUnbResPtr); for (int i = 0; i < treeOutResidualsUnbinned->GetEntries(); ++i) { treeOutResidualsUnbinned->GetEntry(i); prev->treeOutResidualsUnbinned->Fill(); diff --git a/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h b/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h index b109a610f60b5..a3f9f3fe2267c 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h +++ b/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h @@ -29,7 +29,9 @@ #pragma link C++ class o2::tpc::TrackResiduals::VoxRes + ; #pragma link C++ class o2::tpc::TrackResiduals::VoxStats + ; #pragma link C++ class o2::tpc::UnbinnedResid + ; +#pragma link C++ class o2::tpc::DetInfoResid + ; #pragma link C++ class std::vector < o2::tpc::UnbinnedResid> + ; +#pragma link C++ class std::vector < o2::tpc::DetInfoResid> + ; #pragma link C++ class std::vector < o2::tpc::TrackResiduals::LocalResid> + ; #pragma link C++ class std::vector < o2::tpc::TrackResiduals::VoxStats> + ; #pragma link C++ class o2::tpc::ResidualAggregator + ; diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index d13f24ad728fd..cd5e3960160a6 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -18,7 +18,9 @@ #include "SpacePoints/TrackInterpolation.h" #include "SpacePoints/TrackResiduals.h" #include "ITStracking/IOUtils.h" +#include "ITSBase/GeometryTGeo.h" #include "TPCBase/ParameterElectronics.h" +#include "TOFBase/Geo.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/Defs.h" #include "DataFormatsTRD/Constants.h" @@ -30,6 +32,12 @@ #include "TMath.h" #include "DataFormatsTPC/VDriftCorrFact.h" #include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include "GPUO2InterfaceUtils.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUParam.h" +#include "GPUParam.inc" #include #include #include @@ -38,6 +46,71 @@ using namespace o2::tpc; using GTrackID = o2::dataformats::GlobalTrackID; using DetID = o2::detectors::DetID; +bool UnbinnedResid::gInitDone = false; + +float UnbinnedResid::getAlpha() const +{ + if (!isITS()) { + return o2::math_utils::sector2Angle(sec % 18); + } + // ITS alpha repends on the chip ID + checkInitDone(); + return o2::its::GeometryTGeo::Instance()->getSensorRefAlpha(channel); +} + +float UnbinnedResid::getX() const +{ + if (isTPC()) { + return param::RowX[row]; + } + checkInitDone(); + if (isITS()) { + return o2::its::GeometryTGeo::Instance()->getSensorRefX(channel); // ITS X repends on the chip ID + } + if (isTRD()) { + auto geo = o2::trd::Geometry::instance(); + ROOT::Math::Impl::Transform3D::Point local{geo->cdrHght() - 0.5 - 0.279, 0., 0.}; // see TrackletTransformer::transformTracklet + return (geo->getMatrixT2L(channel) ^ local).X(); + } + if (isTOF()) { + int det[5]; + o2::tof::Geo::getVolumeIndices(channel + sec * o2::tof::Geo::NPADSXSECTOR, det); + float pos[3] = {0.f, 0.f, 0.f}; + o2::tof::Geo::getPos(det, pos); + float posl[3] = {pos[0], pos[1], pos[2]}; + o2::tof::Geo::rotateToSector(pos, sec); + return pos[2]; // coordinates in sector frame: note that the rotation above puts z in pos[1], the radial coordinate in pos[2], and the tangent coordinate in pos[0] (this is to match the TOF residual system, where we don't use the radial component), so we swap their positions. + } + LOGP(fatal, "Did not recognize detector type: row:{}, sec:{}, channel:{}", row, sec, channel); + return 0.; +} + +void UnbinnedResid::checkInitDone() +{ + if (!gInitDone) { + LOGP(warn, "geometry initialization was not done, doing this for the current timestamp"); + init(); + if (!gInitDone) { + LOGP(fatal, "geometry initialization failed"); + } + } +} + +void UnbinnedResid::init(long timestamp) +{ + if (gInitDone) { + LOGP(warn, "Initialization was already done"); + return; + } + if (!gGeoManager) { + o2::ccdb::BasicCCDBManager::instance().getSpecific("GLO/Config/GeometryAligned", timestamp); + } + auto geoTRD = o2::trd::Geometry::instance(); + geoTRD->createPadPlaneArray(); + geoTRD->createClusterMatrixArray(); + gInitDone = true; +} + void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap) { // perform initialization @@ -53,7 +126,9 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da mFastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0)); mBz = o2::base::Propagator::Instance()->getNominalBz(); - mRecoParam.setBfield(mBz); + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(mBz, &config.configReconstruction); mGeoTRD = o2::trd::Geometry::instance(); mParams = &SpacePointsCalibConfParam::Instance(); @@ -65,6 +140,9 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da mTrackTypes.insert({GTrackID::ITSTPCTOF, 2}); mTrackTypes.insert({GTrackID::ITSTPCTRDTOF, 3}); + auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + mTPCParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); mInitDone = true; LOGP(info, "Done initializing TrackInterpolation. Configured track input: {}. Track input specifically for map: {}", GTrackID::getSourcesNames(mSourcesConfigured), mSingleSourcesConfigured ? "identical" : GTrackID::getSourcesNames(mSourcesConfiguredMap)); @@ -150,6 +228,9 @@ void TrackInterpolation::prepareInputTrackSample(const o2::globaltracking::RecoC int nv = vtxRefs.size() - 1; GTrackID::mask_t allowedSources = GTrackID::getSourcesMask("ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF") & mSourcesConfigured; constexpr std::array SrcFast = {int(GTrackID::ITSTPCTRD), int(GTrackID::ITSTPCTOF), int(GTrackID::ITSTPCTRDTOF)}; + if (mParams->refitITS) { + mITSRefitSeedID.resize(mRecoCont->getITSTracks().size(), -1); + } for (int iv = 0; iv < nv; iv++) { LOGP(debug, "processing PV {} of {}", iv, nv); @@ -210,6 +291,7 @@ void TrackInterpolation::prepareInputTrackSample(const o2::globaltracking::RecoC mGIDtables.push_back(gidTable); mTrackTimes.push_back(pv.getTimeStamp().getTimeStamp()); mTrackIndices[mTrackTypes[vid.getSource()]].push_back(nTrackSeeds++); + mTrackPVID.push_back(iv); } } } @@ -241,7 +323,11 @@ void TrackInterpolation::process() // set the input containers mTPCTracksClusIdx = mRecoCont->getTPCTracksClusterRefs(); mTPCClusterIdxStruct = &mRecoCont->getTPCClusters(); - if (mDumpTrackPoints) { + int nbOccTOT = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mTPCParam.get()); + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mTPCParam.get(), mNHBPerTF, mRecoCont->occupancyMapTPC.data(), nbOccTOT); + mNTPCOccBinLength = mTPCParam->rec.tpc.occupancyMapTimeBins; + mNTPCOccBinLengthInv = 1.f / mNTPCOccBinLength; + { if (!mITSDict) { LOG(error) << "No ITS dictionary available"; return; @@ -272,10 +358,14 @@ void TrackInterpolation::process() trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].end()); trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].end()); - int nSeeds = mSeeds.size(); + int nSeeds = mSeeds.size(), lastChecked = 0; + mParentID.clear(); + mParentID.resize(nSeeds, -1); + int maxOutputTracks = (mMaxTracksPerTF >= 0) ? mMaxTracksPerTF + mAddTracksForMapPerTF : nSeeds; mTrackData.reserve(maxOutputTracks); mClRes.reserve(maxOutputTracks * param::NPadRows); + mDetInfoRes.reserve(maxOutputTracks * param::NPadRows); bool maxTracksReached = false; for (int iSeed = 0; iSeed < nSeeds; ++iSeed) { if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) { @@ -286,51 +376,66 @@ void TrackInterpolation::process() if (mParams->enableTrackDownsampling && !isTrackSelected(mSeeds[seedIndex])) { continue; } + auto addPart = [this, seedIndex](GTrackID::Source src) { + this->mGIDs.push_back(this->mGIDtables[seedIndex][src]); + this->mGIDtables.push_back(this->mRecoCont->getSingleDetectorRefs(this->mGIDs.back())); + this->mTrackTimes.push_back(this->mTrackTimes[seedIndex]); + this->mSeeds.push_back(this->mSeeds[seedIndex]); + this->mParentID.push_back(seedIndex); // store parent seed id + this->mTrackPVID.push_back(this->mTrackPVID[seedIndex]); + }; + + GTrackID::mask_t partsAdded; if (!mSingleSourcesConfigured && !mSourcesConfiguredMap[mGIDs[seedIndex].getSource()]) { auto src = findValidSource(mSourcesConfiguredMap, static_cast(mGIDs[seedIndex].getSource())); if (src == GTrackID::ITSTPCTRD || src == GTrackID::ITSTPC) { - LOGP(debug, "process: Found valid source {}", GTrackID::getSourceName(src)); - mGIDs.push_back(mGIDtables[seedIndex][src]); - mGIDtables.push_back(mRecoCont->getSingleDetectorRefs(mGIDs.back())); - mTrackTimes.push_back(mTrackTimes[seedIndex]); - mSeeds.push_back(mSeeds[seedIndex]); + LOGP(debug, "process {}: Found valid source {} for {} | nseeds:{} mSeeds:{} used: {}", iSeed, GTrackID::getSourceName(src), GTrackID::getSourceName(mGIDs[seedIndex].getSource()), nSeeds, mSeeds.size(), mTrackDataCompact.size()); + addPart(src); } } if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF) { - LOG(debug) << "We already have reached mMaxTracksPerTF, but we continue to create seeds until mAddTracksForMapPerTF is also reached"; + if (!maxTracksReached) { + LOGP(info, "We already have reached mMaxTracksPerTF={}, but we continue to create seeds until mAddTracksForMapPerTF={} is also reached, iSeed: {} of {} inital seeds", mMaxTracksPerTF, mAddTracksForMapPerTF, iSeed, nSeeds); + } + maxTracksReached = true; continue; } if (mGIDs[seedIndex].includesDet(DetID::TRD) || mGIDs[seedIndex].includesDet(DetID::TOF)) { interpolateTrack(seedIndex); + LOGP(debug, "interpolateTrack {} {}, accepted: {}", iSeed, GTrackID::getSourceName(mGIDs[seedIndex].getSource()), mTrackDataCompact.size()); if (mProcessSeeds) { - if (mGIDs[seedIndex].includesDet(DetID::TRD) && mGIDs[seedIndex].includesDet(DetID::TOF)) { - mGIDs.push_back(mGIDtables[seedIndex][GTrackID::ITSTPCTRD]); - mGIDtables.push_back(mRecoCont->getSingleDetectorRefs(mGIDs.back())); - mTrackTimes.push_back(mTrackTimes[seedIndex]); - mSeeds.push_back(mSeeds[seedIndex]); + if (mGIDs[seedIndex].includesDet(DetID::TRD) && mGIDs[seedIndex].includesDet(DetID::TOF) && !partsAdded[GTrackID::ITSTPCTRD]) { + addPart(GTrackID::ITSTPCTRD); + } + if (!partsAdded[GTrackID::ITSTPC]) { + addPart(GTrackID::ITSTPC); } - mGIDs.push_back(mGIDtables[seedIndex][GTrackID::ITSTPC]); - mGIDtables.push_back(mRecoCont->getSingleDetectorRefs(mGIDs.back())); - mTrackTimes.push_back(mTrackTimes[seedIndex]); - mSeeds.push_back(mSeeds[seedIndex]); } } else { extrapolateTrack(seedIndex); + LOGP(debug, "extrapolateTrack {} {}, accepted: {}", iSeed, GTrackID::getSourceName(mGIDs[seedIndex].getSource()), mTrackDataCompact.size()); } + lastChecked = iSeed; } - if (mSeeds.size() > nSeeds) { - LOGP(info, "Up to {} tracks out of {} additional seeds will be processed", mAddTracksForMapPerTF, mSeeds.size() - nSeeds); + std::vector remSeeds; + if (mSeeds.size() > ++lastChecked) { + remSeeds.resize(mSeeds.size() - lastChecked); + std::iota(remSeeds.begin(), remSeeds.end(), lastChecked); + std::shuffle(remSeeds.begin(), remSeeds.end(), g); + LOGP(info, "Up to {} tracks out of {} additional seeds will be processed in random order, of which {} are stripped versions, accepted seeds: {}", mAddTracksForMapPerTF, remSeeds.size(), mSeeds.size() - nSeeds, mTrackDataCompact.size()); } - for (int iSeed = nSeeds; iSeed < (int)mSeeds.size(); ++iSeed) { - if (!mProcessSeeds && mAddTracksForMapPerTF > 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) { - LOG(info) << "Maximum number of additional tracks per TF reached. Skipping the remaining " << mSeeds.size() - iSeed << " tracks."; + int extraChecked = 0; + for (int iSeed : remSeeds) { + if (mAddTracksForMapPerTF > 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) { + LOGP(info, "Maximum number {} of additional tracks per TF reached. Skipping the remaining {} tracks", mAddTracksForMapPerTF, remSeeds.size() - extraChecked); break; } - // this loop will only be entered in case mProcessSeeds is set - LOGP(debug, "Processing additional track {}", mGIDs[iSeed].asString()); + extraChecked++; if (mGIDs[iSeed].includesDet(DetID::TRD) || mGIDs[iSeed].includesDet(DetID::TOF)) { interpolateTrack(iSeed); + LOGP(debug, "extra check {} of {}, seed {} interpolateTrack {}, used: {}", extraChecked, remSeeds.size(), iSeed, GTrackID::getSourceName(mGIDs[iSeed].getSource()), mTrackDataCompact.size()); } else { + LOGP(debug, "extra check {} of {}, seed {} extrapolateTrack {}, used: {}", extraChecked, remSeeds.size(), iSeed, GTrackID::getSourceName(mGIDs[iSeed].getSource()), mTrackDataCompact.size()); extrapolateTrack(iSeed); } } @@ -342,6 +447,8 @@ void TrackInterpolation::interpolateTrack(int iSeed) { LOGP(debug, "Starting track interpolation for GID {}", mGIDs[iSeed].asString()); TrackData trackData; + o2::trd::Tracklet64 trkl64; + o2::trd::CalibratedTracklet trklCalib; std::unique_ptr trackDataExtended; std::vector clusterResiduals; auto propagator = o2::base::Propagator::Instance(); @@ -361,9 +468,13 @@ void TrackInterpolation::interpolateTrack(int iSeed) (*trackDataExtended).clsITS.push_back(clsITS); } } + if (mParams->refitITS && !refITSTrack(gidTable[GTrackID::ITS], iSeed)) { + return; + } trackData.gid = mGIDs[iSeed]; trackData.par = mSeeds[iSeed]; - auto& trkWork = mSeeds[iSeed]; + auto trkWork = mSeeds[iSeed]; + o2::track::TrackPar trkInner{trkWork}; // reset the cache array (sufficient to set cluster available to zero) for (auto& elem : mCache) { elem.clAvailable = 0; @@ -371,7 +482,9 @@ void TrackInterpolation::interpolateTrack(int iSeed) trackData.clIdx.setFirstEntry(mClRes.size()); // reference the first cluster residual belonging to this track float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS; - // store the TPC cluster positions in the cache + // store the TPC cluster positions in the cache, as well as dedx info + std::array, constants::MAXGLOBALPADROW> mCacheDEDX{}; + std::array multBins{}; for (int iCl = trkTPC.getNClusterReferences(); iCl--;) { uint8_t sector, row; uint32_t clusterIndexInRow; @@ -384,6 +497,12 @@ void TrackInterpolation::interpolateTrack(int iSeed) mCache[row].clY = clTPCYZ[0]; mCache[row].clZ = clTPCYZ[1]; mCache[row].clAngle = o2::math_utils::sector2Angle(sector); + mCacheDEDX[row].first = clTPC.getQtot(); + mCacheDEDX[row].second = clTPC.getQmax(); + int imb = int(clTPC.getTime() * mNTPCOccBinLengthInv); + if (imb < mTPCParam->occupancyMapSize) { + multBins[row] = 1 + std::max(0, imb); + } } // extrapolate seed through TPC and store track position at each pad row @@ -422,10 +541,13 @@ void TrackInterpolation::interpolateTrack(int iSeed) LOG(debug) << "Failed to rotate into TOF cluster sector frame"; return; } - float clTOFX = clTOF.getX(); - std::array clTOFYZ{clTOF.getY(), clTOF.getZ()}; + float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()}; + if (!clTOF.isInNominalSector()) { + o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOFSec); // go from the aligned to nominal sector frame + } + std::array clTOFYZ{clTOFxyz[1], clTOFxyz[2]}; std::array clTOFCov{mParams->sigYZ2TOF, 0.f, mParams->sigYZ2TOF}; // assume no correlation between y and z and equal cluster error sigma^2 = (3cm)^2 / 12 - if (!propagator->PropagateToXBxByBz(trkWork, clTOFX, mParams->maxSnp, mParams->maxStep, mMatCorr)) { + if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) { LOG(debug) << "Failed final propagation to TOF radius"; return; } @@ -443,41 +565,15 @@ void TrackInterpolation::interpolateTrack(int iSeed) (*trackDataExtended).trkTRD = trkTRD; } for (int iLayer = o2::trd::constants::NLAYER - 1; iLayer >= 0; --iLayer) { - int trkltIdx = trkTRD.getTrackletIndex(iLayer); - if (trkltIdx < 0) { - // no TRD tracklet in this layer + std::array trkltTRDYZ{}; + std::array trkltTRDCov{}; + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, &trkltTRDCov); + if (res == -1) { // no TRD tracklet in this layer continue; } - const auto& trdSP = mRecoCont->getTRDCalibratedTracklets()[trkltIdx]; - const auto& trdTrklt = mRecoCont->getTRDTracklets()[trkltIdx]; - if (mDumpTrackPoints) { - (*trackDataExtended).trkltTRD.push_back(trdTrklt); - (*trackDataExtended).clsTRD.push_back(trdSP); - } - auto trkltDet = trdTrklt.getDetector(); - auto trkltSec = trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK); - if (trkltSec != o2::math_utils::angle2Sector(trkWork.getAlpha())) { - if (!trkWork.rotate(o2::math_utils::sector2Angle(trkltSec))) { - LOG(debug) << "Track could not be rotated in TRD tracklet coordinate system in layer " << iLayer; - return; - } - } - if (!propagator->PropagateToXBxByBz(trkWork, trdSP.getX(), mParams->maxSnp, mParams->maxStep, mMatCorr)) { - LOG(debug) << "Failed propagation to TRD layer " << iLayer; + if (res < -1) { // failed to reach this layer return; } - - const auto* pad = mGeoTRD->getPadPlane(trkltDet); - float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees - float tiltCorrUp = tilt * (trdSP.getZ() - trkWork.getZ()); - float zPosCorrUp = trdSP.getZ() + mRecoParam.getZCorrCoeffNRC() * trkWork.getTgl(); // maybe Z can be corrected on avarage already by the tracklet transformer? - float padLength = pad->getRowSize(trdTrklt.getPadRow()); - if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(trdSP.getZ() - trkWork.getZ()) < padLength))) { - tiltCorrUp = 0.f; - } - std::array trkltTRDYZ{trdSP.getY() - tiltCorrUp, zPosCorrUp}; - std::array trkltTRDCov; - mRecoParam.recalcTrkltCov(tilt, trkWork.getSnp(), pad->getRowSize(trdTrklt.getPadRow()), trkltTRDCov); if (!trkWork.update(trkltTRDYZ, trkltTRDCov)) { LOG(debug) << "Failed to update track at TRD layer " << iLayer; return; @@ -488,6 +584,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) if (mDumpTrackPoints) { (*trackDataExtended).trkOuter = trkWork; } + auto trkOuter = trkWork; // outer param // go back through the TPC and store updated track positions bool outerParamStored = false; @@ -552,7 +649,22 @@ void TrackInterpolation::interpolateTrack(int iSeed) trackData.nClsTPC = trkTPC.getNClusterReferences(); trackData.nClsITS = trkITS.getNumberOfClusters(); trackData.nTrkltsTRD = gidTable[GTrackID::TRD].isIndexSet() ? mRecoCont->getITSTPCTRDTrack(gidTable[GTrackID::ITSTPCTRD]).getNtracklets() : 0; - trackData.clAvailTOF = gidTable[GTrackID::TOF].isIndexSet() ? 1 : 0; + + double t0forTOF = 0.; // to be set if TOF is matched + float t0forTOFwithinBC = 0.f; + float t0forTOFres = 9999.f; + + if (gidTable[GTrackID::TOF].isIndexSet()) { + const auto& tofMatch = mRecoCont->getTOFMatch(mGIDs[iSeed]); + ULong64_t bclongtof = (tofMatch.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV; + t0forTOF = tofMatch.getFT0Best(); // setting t0 for TOF + t0forTOFwithinBC = t0forTOF - bclongtof * o2::tof::Geo::BC_TIME_INPS; + t0forTOFres = tofMatch.getFT0BestRes(); + trackData.deltaTOF = tofMatch.getSignal() - t0forTOF - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); + trackData.clAvailTOF = uint16_t(t0forTOFres); + } else { + trackData.clAvailTOF = 0; + } trackData.dEdxTPC = trkTPC.getdEdx().dEdxTotTPC; TrackParams params; // for refitted track parameters and flagging rejected clusters @@ -566,7 +678,6 @@ void TrackInterpolation::interpolateTrack(int iSeed) // skip masked cluster residual continue; } - ++nClValidated; const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp)); const auto dy = clusterResiduals[iCl].dy; const auto dz = clusterResiduals[iCl].dz; @@ -575,18 +686,165 @@ void TrackInterpolation::interpolateTrack(int iSeed) const auto sec = clusterResiduals[iCl].sec; if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, sec); + mDetInfoRes.emplace_back().setTPC(mCacheDEDX[iRow].first, mCacheDEDX[iRow].second); // qtot, qmax + ++nClValidated; } else { ++mRejectedResiduals; } } trackData.clIdx.setEntries(nClValidated); + + // store multiplicity info + for (int ist = 0; ist < NSTACKS; ist++) { + int mltBinMin = 0x7ffff, mltBinMax = -1, prevBin = -1; + for (int ir = STACKROWS[ist]; ir < STACKROWS[ist + 1]; ir++) { + if (multBins[ir] != prevBin && multBins[ir] > 0) { // there is a cluster different from previous one + prevBin = multBins[ir]; + if (multBins[ir] > mltBinMax) { + mltBinMax = multBins[ir]; + } + if (multBins[ir] < mltBinMin) { + mltBinMin = multBins[ir]; + } + } + } + if (--mltBinMin >= 0) { // we were offsetting bin IDs by 1! + float avMlt = 0; + for (int ib = mltBinMin; ib < mltBinMax; ib++) { + avMlt += mTPCParam->occupancyMap[ib]; + } + avMlt /= (mltBinMax - mltBinMin); + trackData.setMultStack(avMlt, ist); + } + } + + bool stopPropagation = !mExtDetResid; + if (!stopPropagation) { + // do we have TRD residuals to add? + trkWork = trkOuter; + if (gidTable[GTrackID::TRD].isIndexSet()) { + const auto& trkTRD = mRecoCont->getITSTPCTRDTrack(gidTable[GTrackID::ITSTPCTRD]); + for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) { + std::array trkltTRDYZ{}; + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib); + if (res == -1) { // no traklet on this layer + continue; + } + if (res < -1) { // failed to reach this layer + stopPropagation = true; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = trkltTRDYZ[0] - trkWork.getY(); + auto dz = trkltTRDYZ[1] - trkWork.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 160 + iLayer, o2::math_utils::angle2Sector(trkWork.getAlpha()), (short)res); + mDetInfoRes.emplace_back().setTRD(trkl64.getQ0(), trkl64.getQ1(), trkl64.getQ2(), trklCalib.getDy()); // q0,q1,q2,slope + trackData.nExtDetResid++; + } + } + } + + // do we have TOF residual to add? + while (gidTable[GTrackID::TOF].isIndexSet() && !stopPropagation) { + const auto& clTOF = mRecoCont->getTOFClusters()[gidTable[GTrackID::TOF]]; + float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()}; + if (!clTOF.isInNominalSector()) { + o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOF.getCount()); // go from the aligned to nominal sector frame + } + const float clTOFAlpha = o2::math_utils::sector2Angle(clTOF.getCount()); + if (trkWork.getAlpha() != clTOFAlpha && !trkWork.rotate(clTOFAlpha)) { + LOG(debug) << "Failed to rotate into TOF cluster sector frame"; + stopPropagation = true; + break; + } + if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOG(debug) << "Failed final propagation to TOF radius"; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = clTOFxyz[1] - trkWork.getY(); + auto dz = clTOFxyz[2] - trkWork.getZ(); + // get seeding track time + + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 170, clTOF.getCount(), clTOF.getPadInSector()); + // get seeding track time + if (!gidTable[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + float tdif = static_cast(clTOF.getTime() - t0forTOF); // time in \mus wrt interaction time0 + mDetInfoRes.emplace_back().setTOF(tdif * 1e-6); + trackData.nExtDetResid++; + } + break; + } + + // add ITS residuals + while (!stopPropagation) { + auto& trkWorkITS = trkInner; // this is ITS outer param + auto nCl = trkITS.getNumberOfClusters(); + auto clEntry = trkITS.getFirstClusterEntry(); + auto geom = o2::its::GeometryTGeo::Instance(); + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]]; + int chip = cls.getSensorID(); + float chipX, chipAlpha; + geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha); + if (!trkWorkITS.rotate(chipAlpha) || !propagator->PropagateToXBxByBz(trkWorkITS, chipX, mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOGP(debug, "Failed final propagation to ITS X={} alpha={}", chipX, chipAlpha); + stopPropagation = true; + break; + } + float tgPhi = trkWorkITS.getSnp() / std::sqrt((1.f - trkWorkITS.getSnp()) * (1.f + trkWorkITS.getSnp())); + auto dy = cls.getY() - trkWorkITS.getY(); + auto dz = cls.getZ() - trkWorkITS.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWorkITS.getY(), trkWorkITS.getZ(), 180 + geom->getLayer(cls.getSensorID()), -1, cls.getSensorID()); + mDetInfoRes.emplace_back(); // empty placeholder + trackData.nExtDetResid++; + } + } + if (!stopPropagation) { // add residual to PV + const auto& pv = mRecoCont->getPrimaryVertices()[mTrackPVID[iSeed]]; + o2::math_utils::Point3D vtx{pv.getX(), pv.getY(), pv.getZ()}; + if (!propagator->propagateToDCA(vtx, trkWorkITS, mBz, mParams->maxStep, mMatCorr)) { + LOGP(debug, "Failed propagation to DCA to PV ({} {} {}), {}", pv.getX(), pv.getY(), pv.getZ(), trkWorkITS.asString()); + stopPropagation = true; + break; + } + // rotate PV to the track frame + float sn, cs, alpha = trkWorkITS.getAlpha(); + math_utils::detail::bringToPMPi(alpha); + math_utils::detail::sincos(alpha, sn, cs); + float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z(); + auto dy = yv - trkWorkITS.getY(); + auto dz = zv - trkWorkITS.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && abs(xv) < param::MaxVtxX) { + short compXV = static_cast(xv * 0x7fff / param::MaxVtxX); + mClRes.emplace_back(dy, dz, alpha / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV); + if (!gidTable[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + float tdif = pv.getTimeStamp().getTimeStamp() - mRecoCont->getTPCITSTrack(gidTable[GTrackID::ITSTPC]).getTimeMUS().getTimeStamp(); + mDetInfoRes.emplace_back().setPV(tdif); // time in \mus wrt seeding ITS-TPC track + trackData.nExtDetResid++; + } + } + break; + } + } + + mGIDsSuccess.push_back(mGIDs[iSeed]); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); mTrackData.push_back(std::move(trackData)); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); + (*trackDataExtended).nExtDetResid = trackData.nExtDetResid; mTrackDataExtended.push_back(std::move(*trackDataExtended)); } - mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(mClRes.size() - nClValidated, nClValidated, mGIDs[iSeed].getSource()); } if (mParams->writeUnfiltered) { TrackData trkDataTmp = trackData; @@ -597,12 +855,67 @@ void TrackInterpolation::interpolateTrack(int iSeed) } } +int TrackInterpolation::processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLayer, o2::track::TrackParCov& trkWork, + std::array* trkltTRDYZ, std::array* trkltTRDCov, TrackData* trkData, + o2::trd::Tracklet64* trk64, o2::trd::CalibratedTracklet* trkCalib) +{ + // return chamber ID (0:539) in case of successful processing, -1 if there is no TRD tracklet at given layer, -2 if processing failed + int trkltIdx = trkTRD.getTrackletIndex(iLayer); + if (trkltIdx < 0) { + return -1; // no TRD tracklet in this layer + } + const auto& trdSP = mRecoCont->getTRDCalibratedTracklets()[trkltIdx]; + const auto& trdTrklt = mRecoCont->getTRDTracklets()[trkltIdx]; + auto trkltDet = trdTrklt.getDetector(); + auto trkltSec = trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK); + if (trkltSec != o2::math_utils::angle2Sector(trkWork.getAlpha())) { + if (!trkWork.rotate(o2::math_utils::sector2Angle(trkltSec))) { + LOG(debug) << "Track could not be rotated in TRD tracklet coordinate system in layer " << iLayer; + return -2; + } + } + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(trkWork, trdSP.getX(), mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOG(debug) << "Failed propagation to TRD layer " << iLayer; + return -2; + } + if (trkltTRDYZ) { + const auto* pad = mGeoTRD->getPadPlane(trkltDet); + float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees + float tiltCorrUp = tilt * (trdSP.getZ() - trkWork.getZ()); + float zPosCorrUp = trdSP.getZ() + mRecoParam.getZCorrCoeffNRC() * trkWork.getTgl(); // maybe Z can be corrected on avarage already by the tracklet transformer? + float padLength = pad->getRowSize(trdTrklt.getPadRow()); + if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(trdSP.getZ() - trkWork.getZ()) < padLength))) { + tiltCorrUp = 0.f; + } + (*trkltTRDYZ)[0] = trdSP.getY() - tiltCorrUp; + (*trkltTRDYZ)[1] = zPosCorrUp; + if (trkltTRDCov) { + mRecoParam.recalcTrkltCov(tilt, trkWork.getSnp(), pad->getRowSize(trdTrklt.getPadRow()), *trkltTRDCov); + } + } + if (trkData) { + auto slope = trdSP.getDy(); + if (std::abs(slope) < param::MaxTRDSlope) { + trkData->TRDTrkltSlope[iLayer] = slope * 0x7fff / param::MaxTRDSlope; + } + } + if (trk64) { + *trk64 = trdTrklt; + } + if (trkCalib) { + *trkCalib = trdSP; + } + return trkltDet; +} + void TrackInterpolation::extrapolateTrack(int iSeed) { // extrapolate ITS-only track through TPC and store residuals to TPC clusters in the output vectors LOGP(debug, "Starting track extrapolation for GID {}", mGIDs[iSeed].asString()); const auto& gidTable = mGIDtables[iSeed]; TrackData trackData; + o2::trd::Tracklet64 trkl64; + o2::trd::CalibratedTracklet trklCalib; std::unique_ptr trackDataExtended; std::vector clusterResiduals; trackData.clIdx.setFirstEntry(mClRes.size()); @@ -621,15 +934,20 @@ void TrackInterpolation::extrapolateTrack(int iSeed) (*trackDataExtended).clsITS.push_back(clsITS); } } + if (mParams->refitITS && !refITSTrack(gidTable[GTrackID::ITS], iSeed)) { + return; + } trackData.gid = mGIDs[iSeed]; trackData.par = mSeeds[iSeed]; - auto& trkWork = mSeeds[iSeed]; + auto trkWork = mSeeds[iSeed]; float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS; auto propagator = o2::base::Propagator::Instance(); unsigned short rowPrev = 0; // used to calculate dRow of two consecutive cluster residuals unsigned short nMeasurements = 0; uint8_t clRowPrev = constants::MAXGLOBALPADROW; // used to identify and skip split clusters on the same pad row + std::array, constants::MAXGLOBALPADROW> mCacheDEDX{}; + std::array multBins{}; for (int iCl = trkTPC.getNClusterReferences(); iCl--;) { uint8_t sector, row; uint32_t clusterIndexInRow; @@ -661,12 +979,23 @@ void TrackInterpolation::extrapolateTrack(int iSeed) const auto tz = trkWork.getZ(); const auto snp = trkWork.getSnp(); const auto sec = sector; - clusterResiduals.emplace_back(dY, dZ, ty, tz, snp, sec, row - rowPrev); - + mCacheDEDX[row].first = cl.getQtot(); + mCacheDEDX[row].second = cl.getQmax(); rowPrev = row; + int imb = int(cl.getTime() * mNTPCOccBinLengthInv); + if (imb < mTPCParam->occupancyMapSize) { + multBins[row] = 1 + std::max(0, imb); + } ++nMeasurements; } + + TrackParams params; // for refitted track parameters and flagging rejected clusters + if (clusterResiduals.size() > constants::MAXGLOBALPADROW) { + LOGP(warn, "Extrapolated ITS-TPC track and found more residuals than possible ({})", clusterResiduals.size()); + return; + } + trackData.chi2TPC = trkTPC.getChi2(); trackData.chi2ITS = trkITS.getChi2(); trackData.nClsTPC = trkTPC.getNClusterReferences(); @@ -677,40 +1006,190 @@ void TrackInterpolation::extrapolateTrack(int iSeed) (*trackDataExtended).trkOuter = trkWork; } - TrackParams params; // for refitted track parameters and flagging rejected clusters - if (clusterResiduals.size() > constants::MAXGLOBALPADROW) { - LOGP(warn, "Extrapolated ITS-TPC track and found more reesiduals than possible ({})", clusterResiduals.size()); - return; - } if (mParams->skipOutlierFiltering || validateTrack(trackData, params, clusterResiduals)) { - // track is good - int nClValidated = 0; - int iRow = 0; - for (unsigned int iCl = 0; iCl < clusterResiduals.size(); ++iCl) { + // track is good, store TPC part + + int nClValidated = 0, iRow = 0; + unsigned int iCl = 0; + for (iCl = 0; iCl < clusterResiduals.size(); ++iCl) { iRow += clusterResiduals[iCl].dRow; - if (params.flagRej[iCl]) { - // skip masked cluster residual + if (iRow < param::NPadRows && params.flagRej[iCl]) { // skip masked cluster residual continue; } - ++nClValidated; const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp)); const auto dy = clusterResiduals[iCl].dy; const auto dz = clusterResiduals[iCl].dz; const auto y = clusterResiduals[iCl].y; const auto z = clusterResiduals[iCl].z; - const auto sec = clusterResiduals[iCl].sec; if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { - mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, sec); + mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, clusterResiduals[iCl].sec); + mDetInfoRes.emplace_back().setTPC(mCacheDEDX[iRow].first, mCacheDEDX[iRow].second); // qtot, qmax + ++nClValidated; } else { ++mRejectedResiduals; } } trackData.clIdx.setEntries(nClValidated); + + // store multiplicity info + for (int ist = 0; ist < NSTACKS; ist++) { + int mltBinMin = 0x7ffff, mltBinMax = -1, prevBin = -1; + for (int ir = STACKROWS[ist]; ir < STACKROWS[ist + 1]; ir++) { + if (multBins[ir] != prevBin && multBins[ir] > 0) { // there is a cluster + prevBin = multBins[ir]; + if (multBins[ir] > mltBinMax) { + mltBinMax = multBins[ir]; + } + if (multBins[ir] < mltBinMin) { + mltBinMin = multBins[ir]; + } + } + } + if (--mltBinMin >= 0) { // we were offsetting bin IDs by 1! + float avMlt = 0; + for (int ib = mltBinMin; ib < mltBinMax; ib++) { + avMlt += mTPCParam->occupancyMap[ib]; + } + avMlt /= (mltBinMax - mltBinMin); + trackData.setMultStack(avMlt, ist); + } + } + + bool stopPropagation = !mExtDetResid; + if (!stopPropagation) { + // do we have TRD residuals to add? + int iSeedFull = mParentID[iSeed] == -1 ? iSeed : mParentID[iSeed]; + auto gidFull = mGIDs[iSeedFull]; + const auto& gidTableFull = mGIDtables[iSeedFull]; + if (gidTableFull[GTrackID::TRD].isIndexSet()) { + const auto& trkTRD = mRecoCont->getITSTPCTRDTrack(gidTableFull[GTrackID::ITSTPCTRD]); + trackData.nTrkltsTRD = trkTRD.getNtracklets(); + for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) { + std::array trkltTRDYZ{}; + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib); + if (res == -1) { // no traklet on this layer + continue; + } + if (res < -1) { // failed to reach this layer + stopPropagation = true; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = trkltTRDYZ[0] - trkWork.getY(); + auto dz = trkltTRDYZ[1] - trkWork.getZ(); + const auto sec = clusterResiduals[iCl].sec; + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 160 + iLayer, o2::math_utils::angle2Sector(trkWork.getAlpha()), (short)res); + mDetInfoRes.emplace_back().setTRD(trkl64.getQ0(), trkl64.getQ1(), trkl64.getQ2(), trklCalib.getDy()); // q0,q1,q2,slope + trackData.nExtDetResid++; + } + } + } + + // do we have TOF residual to add? + trackData.clAvailTOF = 0; + while (gidTableFull[GTrackID::TOF].isIndexSet() && !stopPropagation) { + const auto& tofMatch = mRecoCont->getTOFMatch(gidFull); + ULong64_t bclongtof = (tofMatch.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV; + double t0forTOF = tofMatch.getFT0Best(); // setting t0 for TOF + float t0forTOFwithinBC = t0forTOF - bclongtof * o2::tof::Geo::BC_TIME_INPS; + float t0forTOFres = tofMatch.getFT0BestRes(); + trackData.deltaTOF = tofMatch.getSignal() - t0forTOF - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); + trackData.clAvailTOF = uint16_t(t0forTOFres); + const auto& clTOF = mRecoCont->getTOFClusters()[gidTableFull[GTrackID::TOF]]; + const float clTOFAlpha = o2::math_utils::sector2Angle(clTOF.getCount()); + float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()}; + if (!clTOF.isInNominalSector()) { + o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOF.getCount()); // go from the aligned to nominal sector frame + } + if (trkWork.getAlpha() != clTOFAlpha && !trkWork.rotate(clTOFAlpha)) { + LOG(debug) << "Failed to rotate into TOF cluster sector frame"; + stopPropagation = true; + break; + } + if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOG(debug) << "Failed final propagation to TOF radius"; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = clTOFxyz[1] - trkWork.getY(); + auto dz = clTOFxyz[2] - trkWork.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 170, clTOF.getCount(), clTOF.getPadInSector()); + // get seeding track time + if (!gidTableFull[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + + float tdif = static_cast(clTOF.getTime() - t0forTOF); // time in \mus wrt interaction time0 + mDetInfoRes.emplace_back().setTOF(tdif * 1e-6); // time in \mus wrt seeding ITS-TPC track + trackData.nExtDetResid++; + } + break; + } + + // add ITS residuals + while (!stopPropagation) { + o2::track::TrackPar trkWorkITS{trackData.par}; // this is ITS outer param + auto nCl = trkITS.getNumberOfClusters(); + auto clEntry = trkITS.getFirstClusterEntry(); + auto geom = o2::its::GeometryTGeo::Instance(); + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]]; + int chip = cls.getSensorID(); + float chipX, chipAlpha; + geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha); + if (!trkWorkITS.rotate(chipAlpha) || !propagator->propagateToX(trkWorkITS, chipX, mBz, mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOGP(debug, "Failed final propagation to ITS X={} alpha={}", chipX, chipAlpha); + stopPropagation = true; + break; + } + float tgPhi = trkWorkITS.getSnp() / std::sqrt((1.f - trkWorkITS.getSnp()) * (1.f + trkWorkITS.getSnp())); + auto dy = cls.getY() - trkWorkITS.getY(); + auto dz = cls.getZ() - trkWorkITS.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWorkITS.getY(), trkWorkITS.getZ(), 180 + geom->getLayer(cls.getSensorID()), -1, cls.getSensorID()); + mDetInfoRes.emplace_back(); // empty placeholder + trackData.nExtDetResid++; + } + } + if (!stopPropagation) { // add residual to PV + const auto& pv = mRecoCont->getPrimaryVertices()[mTrackPVID[iSeed]]; + o2::math_utils::Point3D vtx{pv.getX(), pv.getY(), pv.getZ()}; + if (!propagator->propagateToDCA(vtx, trkWorkITS, mBz, mParams->maxStep, mMatCorr)) { + LOGP(debug, "Failed propagation to DCA to PV ({} {} {}), {}", pv.getX(), pv.getY(), pv.getZ(), trkWorkITS.asString()); + stopPropagation = true; + break; + } + // rotate PV to the track frame + float sn, cs, alpha = trkWorkITS.getAlpha(); + math_utils::detail::bringToPMPi(alpha); + math_utils::detail::sincos(alpha, sn, cs); + float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z(); + auto dy = yv - trkWorkITS.getY(); + auto dz = zv - trkWorkITS.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && abs(xv) < param::MaxVtxX) { + short compXV = static_cast(xv * 0x7fff / param::MaxVtxX); + mClRes.emplace_back(dy, dz, alpha / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV); + if (!gidTableFull[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + float tdif = pv.getTimeStamp().getTimeStamp() - mRecoCont->getTPCITSTrack(gidTableFull[GTrackID::ITSTPC]).getTimeMUS().getTimeStamp(); + mDetInfoRes.emplace_back().setPV(tdif); // time in \mus wrt seeding ITS-TPC track + trackData.nExtDetResid++; + } + } + break; + } + } mTrackData.push_back(std::move(trackData)); mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(mClRes.size() - nClValidated, nClValidated, mGIDs[iSeed].getSource()); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); + (*trackDataExtended).nExtDetResid = trackData.nExtDetResid; mTrackDataExtended.push_back(std::move(*trackDataExtended)); } } @@ -1095,6 +1574,7 @@ void TrackInterpolation::reset() mTrackDataCompact.clear(); mTrackDataExtended.clear(); mClRes.clear(); + mDetInfoRes.clear(); mTrackDataUnfiltered.clear(); mClResUnfiltered.clear(); mGIDsSuccess.clear(); @@ -1105,6 +1585,8 @@ void TrackInterpolation::reset() mGIDtables.clear(); mTrackTimes.clear(); mSeeds.clear(); + mITSRefitSeedID.clear(); + mTrackPVID.clear(); } //______________________________________________ @@ -1118,3 +1600,56 @@ void TrackInterpolation::setTPCVDrift(const o2::tpc::VDriftCorrFact& v) o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mFastTransform, 0, 1.0, mTPCVDriftRef, mTPCDriftTimeOffsetRef); } } + +//______________________________________________ +bool TrackInterpolation::refITSTrack(o2::dataformats::GlobalTrackID gid, int seedID) +{ + // refit ITS track outwards taking PID (unless already refitted) from the seed and reassign to the seed + auto& seed = mSeeds[seedID]; + int refitID = mITSRefitSeedID[gid.getIndex()]; + if (refitID >= 0) { // track was already refitted + if (mSeeds[refitID].getPID() == seed.getPID()) { + seed = mSeeds[refitID]; + } + return true; + } + const auto& trkITS = mRecoCont->getITSTrack(gid); + // fetch clusters + auto nCl = trkITS.getNumberOfClusters(); + auto clEntry = trkITS.getFirstClusterEntry(); + o2::track::TrackParCov track(trkITS); // start from the inner param + track.resetCovariance(); + track.setCov(track.getQ2Pt() * track.getQ2Pt() * track.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + track.setPID(seed.getPID()); + o2::track::TrackPar refLin(track); // and use it also as linearization reference + auto geom = o2::its::GeometryTGeo::Instance(); + auto prop = o2::base::Propagator::Instance(); + for (int iCl = nCl - 1; iCl >= 0; iCl--) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]]; + int chip = cls.getSensorID(); + float chipX, chipAlpha; + geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha); + if (!track.rotate(chipAlpha, refLin, mBz)) { + LOGP(debug, "failed to rotate ITS tracks to alpha={} for the refit: {}", chipAlpha, track.asString()); + return false; + } + if (!prop->propagateToX(track, refLin, cls.getX(), mBz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, o2::base::PropagatorF::MatCorrType::USEMatCorrLUT)) { + LOGP(debug, "failed to propagate ITS tracks to X={}: {}", cls.getX(), track.asString()); + return false; + } + std::array posTF{cls.getY(), cls.getZ()}; + std::array covTF{cls.getSigmaY2(), cls.getSigmaYZ(), cls.getSigmaZ2()}; + if (!track.update(posTF, covTF)) { + LOGP(debug, "failed to update ITS tracks by cluster ({},{})/({},{},{})", track.asString(), cls.getY(), cls.getZ(), cls.getSigmaY2(), cls.getSigmaYZ(), cls.getSigmaZ2()); + return false; + } + if (mParams->shiftRefToCluster) { + refLin.setY(posTF[0]); + refLin.setZ(posTF[1]); + } + } + seed = track; + // memorize that this ITS track was already refitted + mITSRefitSeedID[gid.getIndex()] = seedID; + return true; +} diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h index 15c9a8648a796..cecf3ed4b8dca 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h @@ -25,7 +25,6 @@ #include #include -#include "CommonConstants/MathConstants.h" #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/LaserTrack.h" @@ -74,10 +73,12 @@ class CalibLaserTracks ~CalibLaserTracks() = default; /// process all tracks of one TF - void fill(const gsl::span tracks); + /// \param tp ratio of temperature over pressure + void fill(const gsl::span tracks, float tp = 0); /// process all tracks of one TF - void fill(std::vector const& tracks); + /// \param tp ratio of temperature over pressure + void fill(std::vector const& tracks, float tp = 0); /// process single track void processTrack(const TrackTPC& track); @@ -163,6 +164,8 @@ class CalibLaserTracks float mDriftV{0}; ///< drift velocity used during reconstruction float mTOffsetMUS{0}; ///< time offset in \mus to impose float mZbinWidth{0}; ///< width of a bin in us + float mAvgTP{0}; ///< ratio of average temperature over pressure + float mAvgDriftV{0}; ///< average drift velocity used for the laser track calibration uint64_t mTFstart{0}; ///< start time of processed time frames uint64_t mTFend{0}; ///< end time of processed time frames LtrCalibData mCalibDataTF{}; ///< calibration data for single TF (debugging) @@ -184,7 +187,7 @@ class CalibLaserTracks /// perform fits on the matched z-position pairs to extract the drift velocity correction factor and trigger offset void fillCalibData(LtrCalibData& calibData, const std::vector& pairsA, const std::vector& pairsC); - ClassDefNV(CalibLaserTracks, 1); + ClassDefNV(CalibLaserTracks, 2); }; } // namespace o2::tpc diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h index 20e470702a89a..ff7c763efcd2b 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h @@ -129,7 +129,9 @@ class CalibdEdx /// Compute MIP position from dEdx histograms and save result in the correction container. /// To retrieve the correction call `CalibdEdx::getCalib()` /// \param useGausFits make gaussian fits of dEdx vs tgl instead of fitting the mean dEdx - void finalize(const bool useGausFits = true); + /// \param averageSectors If true, the correction is averaged over all sectors. + /// In this case, no mean-sector scaling is applied when statistics are low. + void finalize(const bool useGausFits = true, const bool averageSectors = false); /// Return calib data histogram const Hist& getHist() const { return mHist; } diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CorrMapParam.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrMapParam.h index 147e5587accbb..4ce0e642f4ea3 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CorrMapParam.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrMapParam.h @@ -29,6 +29,7 @@ struct CorrMapParam : public o2::conf::ConfigurableParamHelper { float lumiMean = 0.; // override TPC corr.map mean lumi (if > 0), disable corrections if < 0 float lumiMeanRef = 0.; // override TPC corr.mapRef mean lumi (if > 0)" float lumiInstFactor = 1.; // scaling to apply to instantaneous lumi from CTP (but not to IDC scaler) + float CTP2IDCFallBackThreshold = 30.; // if needed, interpret map->getLumi() as map->getIDC(), provided map->getLumi() is below this threshold int ctpLumiSource = 0; // CTP lumi source: 0 = LumiInfo.getLumi(), 1 = LumiInfo.getLumiAlt() O2ParamDef(CorrMapParam, "TPCCorrMap"); diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h index 90dc84e618cec..5a11ce3ea24e5 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h @@ -42,6 +42,7 @@ struct CorrectionMapsLoaderGloOpts { int lumiMode = 0; ///< what corrections method to use: 0: classical scaling, 1: Using of the derivative map, 2: Using of the derivative map for MC bool enableMShapeCorrection = false; bool requestCTPLumi = true; //< request CTP Lumi regardless of what is used for corrections scaling + bool checkCTPIDCconsistency = true; //< check the selected CTP or IDC scaling source being consistent with mean scaler of the map bool needTPCScalersWorkflow() const { @@ -63,6 +64,8 @@ class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper void init(o2::framework::InitContext& ic); void copySettings(const CorrectionMapsLoader& src); void updateInverse(); /// recalculate inverse correction + void checkMeanScaleConsistency(float meanLumi, float threshold) const; + float getMapMeanRate(const o2::gpu::TPCFastTransform* mp, bool lumiOverridden) const; static void requestCCDBInputs(std::vector& inputs, std::vector& options, const CorrectionMapsLoaderGloOpts& gloOpts); static void addGlobalOptions(std::vector& options); @@ -76,6 +79,7 @@ class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper float mInstLumiCTPFactor = 1.0; // multiplicative factor for inst. lumi int mLumiCTPSource = 0; // 0: main, 1: alternative CTP lumi source std::unique_ptr mCorrMapMShape{nullptr}; + bool mIDC2CTPFallbackActive = false; // flag indicating that fallback from IDC to CTP scaling is active #endif }; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/IDCCCDBHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/IDCCCDBHelper.h index 1b8ba21774f57..744201205de76 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/IDCCCDBHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/IDCCCDBHelper.h @@ -17,6 +17,7 @@ #define ALICEO2_TPC_IDCCCDBHELPER_H_ #include #include "DataFormatsTPC/Defs.h" +#include "TPCBaseRecSim/PadFlags.h" #include "TPCBase/Sector.h" #include "Rtypes.h" diff --git a/Detectors/TPC/calibration/include/TPCCalibration/IDCFactorization.h b/Detectors/TPC/calibration/include/TPCCalibration/IDCFactorization.h index 1fe6486722d95..510b6c44d613b 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/IDCFactorization.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/IDCFactorization.h @@ -24,6 +24,7 @@ #include "TPCCalibration/IDCContainer.h" #include "TPCCalibration/IDCGroupHelperSector.h" #include "DataFormatsTPC/Defs.h" +#include "TPCBaseRecSim/PadFlags.h" #include namespace o2::tpc diff --git a/Detectors/TPC/calibration/include/TPCCalibration/NeuralNetworkClusterizer.h b/Detectors/TPC/calibration/include/TPCCalibration/NeuralNetworkClusterizer.h deleted file mode 100644 index 196bba644714c..0000000000000 --- a/Detectors/TPC/calibration/include/TPCCalibration/NeuralNetworkClusterizer.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file NeuralNetworkClusterizer.h -/// \brief Fetching neural networks for clusterization from CCDB -/// \author Christian Sonnabend - -#ifndef AliceO2_TPC_NeuralNetworkClusterizer_h -#define AliceO2_TPC_NeuralNetworkClusterizer_h - -#include "CCDB/CcdbApi.h" - -namespace o2::tpc -{ - -class NeuralNetworkClusterizer -{ - public: - NeuralNetworkClusterizer() = default; - void initCcdbApi(std::string url); - void loadIndividualFromCCDB(std::map settings); - - private: - o2::ccdb::CcdbApi ccdbApi; - std::map metadata; - std::map headers; -}; - -} // namespace o2::tpc -#endif diff --git a/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h index 671c2efb78a8f..8317fc6bc68d8 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/PressureTemperatureHelper.h @@ -63,15 +63,35 @@ class PressureTemperatureHelper /// get pressure for given time stamp in ms float getPressure(const ULong64_t timestamp) const { return interpolate(mPressure.second, mPressure.first, timestamp); } + /// manually set the pressure + void setPressure(const std::pair, std::vector>& pressure) { mPressure = pressure; } + + /// manually set the temperature + void setTemperature(const std::pair, std::vector>& temperatureA, const std::pair, std::vector>& temperatureC) + { + mTemperatureA = temperatureA; + mTemperatureC = temperatureC; + } + /// get temperature for given time stamp in ms dataformats::Pair getTemperature(const ULong64_t timestamp) const { return dataformats::Pair{interpolate(mTemperatureA.second, mTemperatureA.first, timestamp), interpolate(mTemperatureC.second, mTemperatureC.first, timestamp)}; } + /// get mean temperature over A and C side + float getMeanTemperature(const ULong64_t timestamp) const; + + // get ratio of temperature over pressure for given time stamp + float getTP(int64_t ts) const; + static constexpr o2::header::DataDescription getDataDescriptionPressure() { return o2::header::DataDescription{"pressure"}; } static constexpr o2::header::DataDescription getDataDescriptionTemperature() { return o2::header::DataDescription{"temperature"}; } + /// get minimum and maximum time stamps of the pressure and temperature data + std::pair getMinMaxTime() const; + protected: static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); static void addOutput(std::vector& outputs, o2::framework::OutputSpec&& osp); + static constexpr float toKelvin(float celsius) { return celsius + 273.15f; } // convert Celsius to Kelvin std::pair, std::vector> mPressure; ///< pressure values for both measurements std::pair, std::vector> mTemperatureA; ///< temperature values A-side diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h index c4028f727983f..2b0aef8820acc 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCVDriftTglCalibration.h @@ -32,6 +32,7 @@ struct TPCVDTglContainer { double driftVFullMean = 0.; static float tOffsetRef; static float driftVRef; + float tp = 0; TPCVDTglContainer(int ntgl, float tglMax, int ndtgl, float dtglMax) { @@ -42,9 +43,12 @@ struct TPCVDTglContainer { { histo = std::make_unique(*(src.histo.get())); entries = src.entries; + tp = src.tp; + driftVFullMean = src.driftVFullMean; } - void fill(const gsl::span> data) + /// \param tp ratio of temperature over pressure + void fill(const gsl::span> data, float currentTemperaturePressure = 0) { if (data.size() < 3) { // first 2 entres always contains the {full and reference VDrift} and {full and reference DriftTimeOffset} used for the TF return; @@ -59,12 +63,14 @@ struct TPCVDTglContainer { } // float vfull = data[0].first, vref = data[0].second; + const float temperaturePressure = (data[0].third == 0) ? currentTemperaturePressure : data[0].third; if (driftVRef == 0.f) { driftVRef = vref; } else if (driftVRef != vref) { LOGP(warn, "data with VDriftRef={} were received while initially was set to {}, keep old one", vref, driftVRef); } driftVFullMean = (driftVFullMean * nTFProc + vfull) / (nTFProc + 1); + tp = (tp * nTFProc + temperaturePressure) / (nTFProc + 1); if (tOffsetRef == 0.f) { tOffsetRef = data[1].first; // assign 1st full toffset as a reference } @@ -73,6 +79,11 @@ struct TPCVDTglContainer { void merge(const TPCVDTglContainer* other) { + const int norm = nTFProc + other->nTFProc; + if (norm > 0) { + tp = (tp * nTFProc + other->tp * other->nTFProc) / norm; + driftVFullMean = (driftVFullMean * nTFProc + other->driftVFullMean * other->nTFProc) / norm; + } entries += other->entries; histo->add(*(other->histo)); LOGP(debug, "Old entries:{} New entries:{} oldSum: {} newSum: {}", other->entries, entries, other->histo->getSum(), histo->getSum()); @@ -82,7 +93,7 @@ struct TPCVDTglContainer { { LOG(info) << "Nentries = " << entries; } - ClassDefNV(TPCVDTglContainer, 1); + ClassDefNV(TPCVDTglContainer, 2); }; class TPCVDriftTglCalibration : public o2::calibration::TimeSlotCalibration diff --git a/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h index a8af81fc65e8b..d600df201f985 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/VDriftHelper.h @@ -18,6 +18,7 @@ #include "GPUCommonRtypes.h" #include "DataFormatsTPC/VDriftCorrFact.h" +#include "TPCCalibration/PressureTemperatureHelper.h" #include #include #include @@ -56,6 +57,7 @@ class VDriftHelper Source getSource() const { return mSource; } static std::string_view getSourceName(Source s) { return SourceNames[s]; } std::string_view getSourceName() const { return SourceNames[mSource]; } + const auto& getPTHelper() const { return mPTHelper; } bool accountCCDBInputs(const o2::framework::ConcreteDataMatcher& matcher, void* obj); void extractCCDBInputs(o2::framework::ProcessingContext& pc, bool laser = true, bool itstpcTgl = true); @@ -63,16 +65,20 @@ class VDriftHelper protected: static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); + bool extractTPForVDrift(VDriftCorrFact& vdrift, int64_t tsStepMS = 100 * 1000); VDriftCorrFact mVDLaser{}; VDriftCorrFact mVDTPCITSTgl{}; VDriftCorrFact mVD{}; - Source mSource{Source::Param}; // update source - bool mUpdated = false; // signal update, must be reset once new value is fetched + Source mSource{Source::Param}; // update source + bool mUpdated = false; // signal update, must be reset once new value is fetched + bool mIsTPScalingPossible = false; // if T/P scaling is possible always perform the updating bool mForceParamDrift = false; // enforce vdrift from gasParam bool mForceParamOffset = false; // enforce offset from DetectorParam + bool mForceTPScaling = false; // enforce T/P scaling from gasParam (scaling disabled by negative T or P) uint32_t mMayRenormSrc = 0xffffffff; // if starting VDrift correction != 1, we will renorm reference in such a way that initial correction is 1.0, flag per source + PressureTemperatureHelper mPTHelper; // helper to extract pressure and temperature from CCDB - ClassDefNV(VDriftHelper, 1); + ClassDefNV(VDriftHelper, 2); }; } // namespace o2::tpc #endif diff --git a/Detectors/TPC/calibration/macro/comparePedestalsAndNoise.C b/Detectors/TPC/calibration/macro/comparePedestalsAndNoise.C index 5f998453d9515..04ba2fdeafc27 100644 --- a/Detectors/TPC/calibration/macro/comparePedestalsAndNoise.C +++ b/Detectors/TPC/calibration/macro/comparePedestalsAndNoise.C @@ -12,11 +12,11 @@ #if !defined(__CLING__) || defined(__ROOTCLING__) #include "TROOT.h" #include "TFile.h" -#include "TPCBase/CalDet.h" +#include "TPCBaseRecSim/CalDet.h" #include "TH1F.h" #include "TH2F.h" #include "TCanvas.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #endif std::tuple getNoiseAndPedestalHistogram(const TString pedestalFile, int roc) diff --git a/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C b/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C index b4894ecf60eb9..45677ac7404ec 100644 --- a/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C +++ b/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C @@ -19,9 +19,9 @@ #include "TH2.h" #include "TFile.h" #include "TPCBase/CalDet.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TPCBase/Utils.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPad.h" #include "TCanvas.h" #include "TH1F.h" diff --git a/Detectors/TPC/calibration/macro/drawPulser.C b/Detectors/TPC/calibration/macro/drawPulser.C index 97d14cfd95a58..3be3a958b0025 100644 --- a/Detectors/TPC/calibration/macro/drawPulser.C +++ b/Detectors/TPC/calibration/macro/drawPulser.C @@ -16,7 +16,7 @@ #include "TH2.h" #include "TFile.h" #include "TPCBase/CalDet.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TPCBase/Utils.h" #include "TPCBase/Mapper.h" #include "TPad.h" diff --git a/Detectors/TPC/calibration/macro/prepareCMFiles.C b/Detectors/TPC/calibration/macro/prepareCMFiles.C index 08880ccbe4862..3bf18a9d14f8f 100644 --- a/Detectors/TPC/calibration/macro/prepareCMFiles.C +++ b/Detectors/TPC/calibration/macro/prepareCMFiles.C @@ -18,7 +18,7 @@ #include "TFile.h" #include "Framework/Logger.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/Mapper.h" #include "TPCBase/CalDet.h" #include "TPCBase/Utils.h" diff --git a/Detectors/TPC/calibration/macro/prepareITFiles.C b/Detectors/TPC/calibration/macro/prepareITFiles.C index eac0355e0ddfd..215ddb7909c8d 100644 --- a/Detectors/TPC/calibration/macro/prepareITFiles.C +++ b/Detectors/TPC/calibration/macro/prepareITFiles.C @@ -21,7 +21,7 @@ #include "TFile.h" #include "Framework/Logger.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/Mapper.h" #include "TPCBase/CalDet.h" #include "TPCBase/Utils.h" diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.C b/Detectors/TPC/calibration/macro/preparePedestalFiles.C index 92bc1456e48d7..894827fffab1e 100644 --- a/Detectors/TPC/calibration/macro/preparePedestalFiles.C +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.C @@ -18,7 +18,7 @@ #include "TFile.h" #include "TROOT.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/Mapper.h" #include "TPCBase/CalDet.h" #include "TPCBase/Utils.h" diff --git a/Detectors/TPC/calibration/src/CalculatedEdx.cxx b/Detectors/TPC/calibration/src/CalculatedEdx.cxx index 60e9ada7794d3..478acda1189c2 100644 --- a/Detectors/TPC/calibration/src/CalculatedEdx.cxx +++ b/Detectors/TPC/calibration/src/CalculatedEdx.cxx @@ -21,7 +21,7 @@ #include "DataFormatsTPC/ClusterNative.h" #include "DetectorsBase/Propagator.h" #include "CCDB/BasicCCDBManager.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CalibdEdxTrackTopologyPol.h" #include "DataFormatsParameters/GRPMagField.h" @@ -566,7 +566,7 @@ void CalculatedEdx::loadCalibsFromCCDB(long runNumberOrTimeStamp, const bool isM mCalibCont.setResidualCorrection(*residualCorr); // set the zero supression threshold map - std::unordered_map>* zeroSupressionThresholdMap = cm.getForTimeStamp>>(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::ConfigFEEPad), tRun); + std::unordered_map>* zeroSupressionThresholdMap = cm.getForTimeStamp>>(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::ConfigFEEPad), tRun); mCalibCont.setZeroSupresssionThreshold(zeroSupressionThresholdMap->at("ThresholdMap")); // set the magnetic field @@ -624,7 +624,7 @@ void CalculatedEdx::setGainMapResidualFromFile(const char* folder, const char* f std::unique_ptr gainMapResidualFile(TFile::Open(fmt::format("{}{}", folder, file).data())); if (!gainMapResidualFile->IsZombie()) { LOGP(info, "Using file: {}", gainMapResidualFile->GetName()); - std::unordered_map>* gainMapResidual = (std::unordered_map>*)gainMapResidualFile->Get(object); + std::unordered_map>* gainMapResidual = (std::unordered_map>*)gainMapResidualFile->Get(object); mCalibCont.setGainMapResidual(gainMapResidual->at("GainMap")); } } @@ -644,7 +644,7 @@ void CalculatedEdx::setZeroSuppressionThresholdFromFile(const char* folder, cons std::unique_ptr zeroSuppressionFile(TFile::Open(fmt::format("{}{}", folder, file).data())); if (!zeroSuppressionFile->IsZombie()) { LOGP(info, "Using file: {}", zeroSuppressionFile->GetName()); - std::unordered_map>* zeroSupressionThresholdMap = (std::unordered_map>*)zeroSuppressionFile->Get(object); + std::unordered_map>* zeroSupressionThresholdMap = (std::unordered_map>*)zeroSuppressionFile->Get(object); mCalibCont.setZeroSupresssionThreshold(zeroSupressionThresholdMap->at("ThresholdMap")); } } diff --git a/Detectors/TPC/calibration/src/CalibLaserTracks.cxx b/Detectors/TPC/calibration/src/CalibLaserTracks.cxx index 1e4218c527f02..da52525328c6c 100644 --- a/Detectors/TPC/calibration/src/CalibLaserTracks.cxx +++ b/Detectors/TPC/calibration/src/CalibLaserTracks.cxx @@ -24,13 +24,13 @@ #include using namespace o2::tpc; -void CalibLaserTracks::fill(std::vector const& tracks) +void CalibLaserTracks::fill(std::vector const& tracks, float tp) { - fill(gsl::span(tracks.data(), tracks.size())); + fill(gsl::span(tracks.data(), tracks.size()), tp); } //______________________________________________________________________________ -void CalibLaserTracks::fill(const gsl::span tracks) +void CalibLaserTracks::fill(const gsl::span tracks, float tp) { // ===| clean up TF data |=== mZmatchPairsTFA.clear(); @@ -63,6 +63,9 @@ void CalibLaserTracks::fill(const gsl::span tracks) mCalibDataTF.firstTime = mTFstart; mCalibDataTF.lastTime = tfEnd; + mAvgTP = (mAvgTP * mCalibData.processedTFs + tp) / (mCalibData.processedTFs + 1); + mAvgDriftV = (mAvgDriftV * mCalibData.processedTFs + mDriftV) / (mCalibData.processedTFs + 1); + // ===| TF counters |=== ++mCalibData.processedTFs; ++mCalibDataTF.processedTFs; @@ -147,6 +150,8 @@ void CalibLaserTracks::processTrack(const TrackTPC& track) << "ltr=" << ltr // matched ideal laser track << "trOutLtr=" << parOutAtLtr // track rotated and propagated to ideal track position << "TPCTracks=" << writeTrack // original TPC track + << "mDriftV=" << mDriftV + << "laserTrackID=" << laserTrackID << "\n"; } } @@ -277,6 +282,18 @@ void CalibLaserTracks::merge(const CalibLaserTracks* other) mCalibData.firstTime = std::min(mCalibData.firstTime, other->mCalibData.firstTime); mCalibData.lastTime = std::max(mCalibData.lastTime, other->mCalibData.lastTime); + if ((mAvgTP > 0) && (other->mAvgTP > 0)) { + mAvgTP = (mAvgTP + other->mAvgTP) / 2.0; + } else if (other->mAvgTP > 0) { + mAvgTP = other->mAvgTP; + } + + if ((mAvgDriftV > 0) && (other->mAvgDriftV > 0)) { + mAvgDriftV = (mAvgDriftV + other->mAvgDriftV) / 2.0; + } else if (other->mAvgDriftV > 0) { + mAvgDriftV = other->mAvgDriftV; + } + sort(mZmatchPairsA); sort(mZmatchPairsC); @@ -296,6 +313,7 @@ void CalibLaserTracks::endTF() << "zPairsA=" << mZmatchPairsTFA << "zPairsC=" << mZmatchPairsTFC << "calibData=" << mCalibDataTF + << "mDriftV=" << mDriftV << "\n"; } } @@ -330,7 +348,7 @@ void CalibLaserTracks::fillCalibData(LtrCalibData& calibData, const std::vector< auto dvA = fit(pairsA, "A-Side"); auto dvC = fit(pairsC, "C-Side"); calibData.creationTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - calibData.refVDrift = mDriftV; + calibData.refVDrift = mAvgDriftV; calibData.dvOffsetA = dvA.x1; calibData.dvCorrectionA = dvA.x2; calibData.nTracksA = uint16_t(pairsA.size()); @@ -340,6 +358,7 @@ void CalibLaserTracks::fillCalibData(LtrCalibData& calibData, const std::vector< calibData.nTracksC = uint16_t(pairsC.size()); calibData.refTimeOffset = mTOffsetMUS; + calibData.tp = mAvgTP; } //______________________________________________________________________________ diff --git a/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx b/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx index 93cdb7c47ee37..094de6b830272 100644 --- a/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx +++ b/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx @@ -22,7 +22,7 @@ #include "CorrectionMapsHelper.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "GPUO2InterfaceRefit.h" -#include "GPUO2Interface.h" +#include "GPUO2ExternalUser.h" #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/VDriftCorrFact.h" #include "DetectorsBase/Propagator.h" diff --git a/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx b/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx index 2d8c34810324b..8a2ad1df19200 100644 --- a/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx +++ b/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx @@ -15,7 +15,7 @@ #include "TPCCalibration/CalibPadGainTracksBase.h" #include "TPCCalibration/IDCDrawHelper.h" #include "TPCBase/ROC.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TPCCalibration/CalibTreeDump.h" #include "TPCBase/Mapper.h" diff --git a/Detectors/TPC/calibration/src/CalibdEdx.cxx b/Detectors/TPC/calibration/src/CalibdEdx.cxx index e1081335c04cb..938ab8ae91065 100644 --- a/Detectors/TPC/calibration/src/CalibdEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibdEdx.cxx @@ -351,7 +351,7 @@ auto ProjectBoostHistoXFastAllSectors(const Hist& hist, std::vector& bin_in // access the bin content specified by bin_indices const float counts = hist.at(bin_indices); - float dEdx = hist.axis(ax::dEdx).value(i); + float dEdx = hist.axis(ax::dEdx).bin(i).center(); // scale the dedx to the mean if (stackMean != nullptr) { @@ -532,7 +532,7 @@ void CalibdEdx::fitHistGaus(TLinearFitter& fitter, CalibdEdxCorrection& corr, co LOGP(info, "Calibration fits took: {}", time.count()); } -void CalibdEdx::finalize(const bool useGausFits) +void CalibdEdx::finalize(const bool useGausFits, const bool averageSectors) { const float entries = minStackEntries(); mCalib.clear(); @@ -565,10 +565,15 @@ void CalibdEdx::finalize(const bool useGausFits) // get mean of each GEM stack CalibdEdxCorrection meanCorr{}; meanCorr.setDims(0); - TLinearFitter meanFitter(0); - meanFitter.SetFormula("1"); - // get the mean dEdx for each stack - fitHist(mHist, meanCorr, meanFitter, mFitCut, mFitLowCutFactor, mFitPasses); + if (averageSectors) { + // set mean dEdx per stack to unity + meanCorr.setUnity(); + } else { + // get the mean dEdx for each stack + TLinearFitter meanFitter(0); + meanFitter.SetFormula("1"); + fitHist(mHist, meanCorr, meanFitter, mFitCut, mFitLowCutFactor, mFitPasses, nullptr, mDebugOutputStreamer.get()); + } if (!useGausFits) { // get higher dimension corrections with projected sectors fitHist(mHist, mCalib, fitter, mFitCut, mFitLowCutFactor, mFitPasses, &meanCorr, mDebugOutputStreamer.get()); @@ -754,17 +759,28 @@ void CalibdEdx::dumpToFile(const char* outFile) CalibdEdx CalibdEdx::readFromFile(const char* inFile) { - TFile f(inFile, "READ"); - auto* obj = (CalibdEdx*)f.Get("calib"); + std::unique_ptr f(TFile::Open(inFile)); + if (!f || f->IsZombie()) { + LOGP(error, "Could not open file: {}", inFile); + CalibdEdx calTmp; + return calTmp; + } + + auto obj = f->Get("calib"); if (!obj) { + LOGP(error, "Could not read CalibdEdx object from file: {}", inFile); CalibdEdx calTmp; return calTmp; } + + THnF* hTmp = f->Get("histogram_data"); + CalibdEdx cal(*obj); - THnF* hTmp = (THnF*)f.Get("histogram_data"); + delete obj; + if (!hTmp) { - CalibdEdx calTmp; - return calTmp; + LOGP(warning, "Could not read histogram from file: {}. Returning empty histogram", inFile); + return cal; } cal.setFromRootHist(hTmp); return cal; diff --git a/Detectors/TPC/calibration/src/CorrectdEdxDistortions.cxx b/Detectors/TPC/calibration/src/CorrectdEdxDistortions.cxx index e5d1f32ad5661..73599e744483c 100644 --- a/Detectors/TPC/calibration/src/CorrectdEdxDistortions.cxx +++ b/Detectors/TPC/calibration/src/CorrectdEdxDistortions.cxx @@ -70,8 +70,8 @@ void o2::tpc::CorrectdEdxDistortions::setLumi(float lumi) LOGP(warn, "Nullptr detected in accessing the correction maps"); return; } - const float lumiAvg = mCorrAvg->getLumi(); - const float lumiDer = mCorrDer->getLumi(); + const float lumiAvg = mCorrAvg->getIDC(); + const float lumiDer = mCorrDer->getIDC(); mScaleDer = (lumi - lumiAvg) / lumiDer; LOGP(info, "Setting mScaleDer: {} for inst lumi: {} avg lumi: {} deriv. lumi: {}", mScaleDer, lumi, lumiAvg, lumiDer); } diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index e13f887cbdc21..038fe3c34e140 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx @@ -12,7 +12,7 @@ #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCCalibration/CorrMapParam.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "Framework/Logger.h" #include "Framework/ProcessingContext.h" #include "Framework/CCDBParamSpec.h" @@ -53,6 +53,37 @@ void CorrectionMapsLoader::extractCCDBInputs(ProcessingContext& pc) o2::ctp::LumiInfo lumiObj; static o2::ctp::LumiInfo lumiPrev; + if (getLumiScaleType() == 2 || mIDC2CTPFallbackActive) { + float tpcScaler = pc.inputs().get("tpcscaler"); + // check if tpcScaler is valid and CTP fallback is allowed + if (tpcScaler == -1.f) { + const bool canUseCTPScaling = mCorrMap && mCorrMapRef && mCorrMap->isIDCSet() && mCorrMapRef->isIDCSet() && mCorrMap->isLumiSet() && mCorrMapRef->isLumiSet(); + if (canUseCTPScaling) { + LOGP(info, "Invalid TPC scaler value {} received for IDC-based scaling! Using CTP fallback", tpcScaler); + mIDC2CTPFallbackActive = true; + setMeanLumi(mCorrMap->getLumi(), false); + setMeanLumiRef(mCorrMapRef->getLumi()); + setLumiScaleType(1); + } else if (mCorrMap) { + // CTP scaling is not possible, dont do any scaling to avoid applying wrong corrections + const float storedIDC = mCorrMap->getIDC(); + LOGP(warning, "Invalid TPC scaler value {} received for IDC-based scaling! CTP fallback not possible, using stored IDC of {} from the map to avoid applying wrong corrections", tpcScaler, storedIDC); + setInstLumi(storedIDC); + } + } else { + if (mIDC2CTPFallbackActive) { + // reset back to normal operation + LOGP(info, "Valid TPC scaler value {} received, switching back to IDC-based scaling", tpcScaler); + mIDC2CTPFallbackActive = false; + setMeanLumi(mCorrMap->getIDC(), false); + setMeanLumiRef(mCorrMapRef->getIDC()); + setLumiScaleType(2); + } + // correct IDC received + setInstLumi(tpcScaler); + } + } + if (getLumiCTPAvailable() && mInstCTPLumiOverride <= 0.) { if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { lumiPrev = lumiObj = pc.inputs().get("CTPLumi"); @@ -67,10 +98,7 @@ void CorrectionMapsLoader::extractCCDBInputs(ProcessingContext& pc) setInstLumi(getInstLumiCTP()); } } - if (getLumiScaleType() == 2) { - float tpcScaler = pc.inputs().get("tpcscaler"); - setInstLumi(tpcScaler); - } + if (getUseMShapeCorrection()) { LOGP(info, "Setting M-Shape map"); const auto mapMShape = pc.inputs().get("mshape"); @@ -138,6 +166,7 @@ void CorrectionMapsLoader::addGlobalOptions(std::vector& option addOption(options, ConfigParamSpec{"corrmap-lumi-mode", o2::framework::VariantType::Int, 0, {"scaling mode: (default) 0 = static + scale * full; 1 = full + scale * derivative; 2 = full + scale * derivative (for MC)"}}); addOption(options, ConfigParamSpec{"enable-M-shape-correction", o2::framework::VariantType::Bool, false, {"Enable M-shape distortion correction"}}); addOption(options, ConfigParamSpec{"disable-ctp-lumi-request", o2::framework::VariantType::Bool, false, {"do not request CTP lumi (regardless what is used for corrections)"}}); + addOption(options, ConfigParamSpec{"disable-lumi-type-consistency-check", o2::framework::VariantType::Bool, false, {"disable check of selected CTP or IDC scaling source being consistent with the map"}}); } //________________________________________________________ @@ -148,6 +177,7 @@ CorrectionMapsLoaderGloOpts CorrectionMapsLoader::parseGlobalOptions(const o2::f tpcopt.lumiMode = opts.get("corrmap-lumi-mode"); tpcopt.enableMShapeCorrection = opts.get("enable-M-shape-correction"); tpcopt.requestCTPLumi = !opts.get("disable-ctp-lumi-request"); + tpcopt.checkCTPIDCconsistency = !opts.get("disable-lumi-type-consistency-check"); if (!tpcopt.requestCTPLumi && tpcopt.lumiType == 1) { LOGP(fatal, "Scaling with CTP Lumi is requested but this input is disabled"); } @@ -176,20 +206,58 @@ bool CorrectionMapsLoader::accountCCDBInputs(const ConcreteDataMatcher& matcher, if (matcher == ConcreteDataMatcher("TPC", "CorrMap", 0)) { setCorrMap((o2::gpu::TPCFastTransform*)obj); mCorrMap->rectifyAfterReadingFromFile(); - if (getMeanLumiOverride() == 0 && mCorrMap->getLumi() > 0.) { - setMeanLumi(mCorrMap->getLumi(), false); + mCorrMap->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); + if (getMeanLumiOverride() != 0) { + if (getLumiScaleType() == 1) { + mCorrMap->setLumi(getMeanLumiOverride()); + LOGP(info, "CorrMap mean lumi rate is overridden to {}", mCorrMap->getLumi()); + } else if (getLumiScaleType() == 2) { + mCorrMap->setIDC(getMeanLumiOverride()); + LOGP(info, "CorrMap mean IDC rate is overridden to {}", mCorrMap->getIDC()); + } + } + float mapMeanRate = 0; + if (getLumiScaleType() == 1) { + mapMeanRate = mCorrMap->getLumi(); + } else if (getLumiScaleType() == 2) { + mapMeanRate = mCorrMap->getIDC(); } - LOGP(debug, "MeanLumiOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiOverride(), mCorrMap->getLumi(), getMeanLumi()); + if (mCheckCTPIDCConsistency) { + checkMeanScaleConsistency(mapMeanRate, mCorrMap->getCTP2IDCFallBackThreshold()); + } + if (getMeanLumiOverride() == 0 && mapMeanRate > 0.) { + setMeanLumi(mapMeanRate, false); + } + LOGP(debug, "MeanLumiOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiOverride(), mapMeanRate, getMeanLumi()); setUpdatedMap(); return true; } if (matcher == ConcreteDataMatcher("TPC", "CorrMapRef", 0)) { setCorrMapRef((o2::gpu::TPCFastTransform*)obj); mCorrMapRef->rectifyAfterReadingFromFile(); + mCorrMapRef->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); + if (getMeanLumiRefOverride() != 0) { + if (getLumiScaleType() == 1) { + mCorrMapRef->setLumi(getMeanLumiRefOverride()); + LOGP(info, "CorrMapRef mean lumi rate is overridden to {}", mCorrMapRef->getLumi()); + } else if (getLumiScaleType() == 2) { + mCorrMapRef->setIDC(getMeanLumiRefOverride()); + LOGP(info, "CorrMapRef mean IDC rate is overridden to {}", mCorrMapRef->getIDC()); + } + } + float mapRefMeanRate = 0; + if (getLumiScaleType() == 1) { + mapRefMeanRate = mCorrMapRef->getLumi(); + } else if (getLumiScaleType() == 2) { + mapRefMeanRate = mCorrMapRef->getIDC(); + } + if (mCheckCTPIDCConsistency) { + checkMeanScaleConsistency(mapRefMeanRate, mCorrMapRef->getCTP2IDCFallBackThreshold()); + } if (getMeanLumiRefOverride() == 0) { - setMeanLumiRef(mCorrMapRef->getLumi()); + setMeanLumiRef(mapRefMeanRate); } - LOGP(debug, "MeanLumiRefOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiRefOverride(), mCorrMapRef->getLumi(), getMeanLumiRef()); + LOGP(debug, "MeanLumiRefOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiRefOverride(), mapRefMeanRate, getMeanLumiRef()); setUpdatedMapRef(); return true; } @@ -217,7 +285,7 @@ bool CorrectionMapsLoader::accountCCDBInputs(const ConcreteDataMatcher& matcher, int scaleType = getLumiScaleType(); const std::array lumiS{"OFF", "CTP", "TPC scaler"}; if (scaleType >= lumiS.size()) { - LOGP(fatal, "Wrong lumi-scale-type provided!"); + LOGP(fatal, "Wrong corrmap-lumi-mode provided!"); } LOGP(info, "TPC correction map params updated: SP corrections: {} (corr.map scaling type={}, override values: lumiMean={} lumiRefMean={} lumiScaleMode={}), CTP Lumi: source={} lumiInstOverride={} , LumiInst scale={} ", @@ -277,6 +345,7 @@ void CorrectionMapsLoader::copySettings(const CorrectionMapsLoader& src) mLumiCTPSource = src.mLumiCTPSource; mLumiScaleMode = src.mLumiScaleMode; mScaleInverse = src.getScaleInverse(); + mIDC2CTPFallbackActive = src.mIDC2CTPFallbackActive; } void CorrectionMapsLoader::updateInverse() @@ -296,4 +365,17 @@ void CorrectionMapsLoader::updateInverse() } } +void CorrectionMapsLoader::checkMeanScaleConsistency(float meanLumi, float threshold) const +{ + if (getLumiScaleType() == 1) { + if (meanLumi < threshold) { + LOGP(fatal, "CTP Lumi scaling source is requested, but the map mean scale {} is below the threshold {}", meanLumi, threshold); + } + } else if (getLumiScaleType() == 2) { + if (meanLumi > threshold) { + LOGP(fatal, "IDC scaling source is requested, but the map mean scale {} is above the threshold {}", meanLumi, threshold); + } + } +} + #endif // #ifndef GPUCA_GPUCODE_DEVICE diff --git a/Detectors/TPC/calibration/src/IDCAverageGroup.cxx b/Detectors/TPC/calibration/src/IDCAverageGroup.cxx index f027a0a7d0056..63ab4d9e537ac 100644 --- a/Detectors/TPC/calibration/src/IDCAverageGroup.cxx +++ b/Detectors/TPC/calibration/src/IDCAverageGroup.cxx @@ -15,12 +15,13 @@ #include "TPCCalibration/IDCDrawHelper.h" #include "CommonUtils/TreeStreamRedirector.h" #include "TPCBase/Mapper.h" +#include "TPCBaseRecSim/PadFlags.h" #include "CommonConstants/MathConstants.h" // root includes #include "TFile.h" #include "TKey.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TH2Poly.h" #include "TCanvas.h" #include "TLatex.h" diff --git a/Detectors/TPC/calibration/src/IDCCCDBHelper.cxx b/Detectors/TPC/calibration/src/IDCCCDBHelper.cxx index a9fb8f0c4675f..189d1035fc767 100644 --- a/Detectors/TPC/calibration/src/IDCCCDBHelper.cxx +++ b/Detectors/TPC/calibration/src/IDCCCDBHelper.cxx @@ -18,7 +18,7 @@ #include "TPCBase/CalDet.h" #include "TPCBase/Mapper.h" #include "CommonUtils/TreeStreamRedirector.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TStyle.h" #include "TLine.h" diff --git a/Detectors/TPC/calibration/src/IDCDrawHelper.cxx b/Detectors/TPC/calibration/src/IDCDrawHelper.cxx index 3a0b11b4a3beb..a5181cc36706d 100644 --- a/Detectors/TPC/calibration/src/IDCDrawHelper.cxx +++ b/Detectors/TPC/calibration/src/IDCDrawHelper.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. #include "TPCCalibration/IDCDrawHelper.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TPCBase/Mapper.h" #include "TH2Poly.h" #include "TCanvas.h" diff --git a/Detectors/TPC/calibration/src/NeuralNetworkClusterizer.cxx b/Detectors/TPC/calibration/src/NeuralNetworkClusterizer.cxx deleted file mode 100644 index bfbb7afc946f8..0000000000000 --- a/Detectors/TPC/calibration/src/NeuralNetworkClusterizer.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file NeuralNetworkClusterizer.cxx -/// \brief Fetching neural networks for clusterization from CCDB -/// \author Christian Sonnabend - -#include -#include "TPCCalibration/NeuralNetworkClusterizer.h" - -using namespace o2::tpc; - -void NeuralNetworkClusterizer::initCcdbApi(std::string url) -{ - ccdbApi.init(url); -} - -void NeuralNetworkClusterizer::loadIndividualFromCCDB(std::map settings) -{ - metadata["inputDType"] = settings["inputDType"]; - metadata["outputDType"] = settings["outputDType"]; - metadata["nnCCDBEvalType"] = settings["nnCCDBEvalType"]; // classification_1C, classification_2C, regression_1C, regression_2C - metadata["nnCCDBWithMomentum"] = settings["nnCCDBWithMomentum"]; // 0, 1 -> Only for regression model - metadata["nnCCDBLayerType"] = settings["nnCCDBLayerType"]; // FC, CNN - if (settings["nnCCDBInteractionRate"] != "" && std::stoi(settings["nnCCDBInteractionRate"]) > 0) { - metadata["nnCCDBInteractionRate"] = settings["nnCCDBInteractionRate"]; - } - if (settings["nnCCDBBeamType"] != "") { - metadata["nnCCDBBeamType"] = settings["nnCCDBBeamType"]; - } - - bool retrieveSuccess = ccdbApi.retrieveBlob(settings["nnCCDBPath"], settings["outputFolder"], metadata, 1, false, settings["outputFile"]); - // headers = ccdbApi.retrieveHeaders(settings["nnPathCCDB"], metadata, 1); // potentially needed to init some local variables - - if (retrieveSuccess) { - LOG(info) << "Network " << settings["nnCCDBPath"] << " retrieved from CCDB, stored at " << settings["outputFile"]; - } else { - LOG(error) << "Failed to retrieve network from CCDB"; - } -} diff --git a/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx b/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx index d9a55e4aed2b9..4f22ef8e35a03 100644 --- a/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx +++ b/Detectors/TPC/calibration/src/PressureTemperatureHelper.cxx @@ -14,7 +14,7 @@ /// \author Matthias Kleiner #include "TPCCalibration/PressureTemperatureHelper.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBTypes.h" #include "Framework/ProcessingContext.h" #include "DataFormatsTPC/DCS.h" #include "Framework/InputRecord.h" @@ -52,14 +52,25 @@ bool PressureTemperatureHelper::accountCCDBInputs(const ConcreteDataMatcher& mat mTemperatureC.second.clear(); for (const auto& dp : temp.statsA.data) { - mTemperatureA.first.emplace_back(dp.value.mean); + mTemperatureA.first.emplace_back(toKelvin(dp.value.mean)); mTemperatureA.second.emplace_back(dp.time); } for (const auto& dp : temp.statsC.data) { - mTemperatureC.first.emplace_back(dp.value.mean); + mTemperatureC.first.emplace_back(toKelvin(dp.value.mean)); mTemperatureC.second.emplace_back(dp.time); } + + // check if temperature data is available + if (mTemperatureA.first.empty() && mTemperatureC.first.empty()) { + float temperature = toKelvin(temp.getMeanTempRaw()); + mTemperatureA.first.emplace_back(temperature); + mTemperatureA.second.emplace_back(0); + mTemperatureC.first.emplace_back(temperature); + mTemperatureC.second.emplace_back(0); + LOGP(warning, "No temperature data available from fit. Using average temperature {} K", temperature); + } + return true; } return false; @@ -117,3 +128,63 @@ void PressureTemperatureHelper::sendPTForTS(o2::framework::ProcessingContext& pc pc.outputs().snapshot(Output{o2::header::gDataOriginTPC, o2::tpc::PressureTemperatureHelper::getDataDescriptionTemperature()}, temp); pc.outputs().snapshot(Output{o2::header::gDataOriginTPC, o2::tpc::PressureTemperatureHelper::getDataDescriptionPressure()}, pressure); } + +float PressureTemperatureHelper::getTP(int64_t ts) const +{ + const float pressure = getPressure(ts); + const auto temp = getMeanTemperature(ts); + if (pressure <= 0) { + LOGP(error, "Pressure {} is zero or negative, cannot compute T/P ratio for timestamp {}", pressure, ts); + return 0; + } + const float tp = temp / pressure; + return tp; +} + +float PressureTemperatureHelper::getMeanTemperature(const ULong64_t timestamp) const +{ + const auto temp = getTemperature(timestamp); + + float sumT = 0; + int w = 0; + constexpr float minTemp = toKelvin(15); + constexpr float maxTemp = toKelvin(25); + if (auto t = temp.first; t > minTemp && t < maxTemp) { + sumT += t; + ++w; + } + if (auto t = temp.second; t > minTemp && t < maxTemp) { + sumT += t; + ++w; + } + + if (w == 0) { + constexpr float defaultTemp = toKelvin(19.6440f); + LOGP(info, "Returning default temperature of {}K", defaultTemp); + return defaultTemp; + } + + const float meanT = sumT / w; + return meanT; +} + +std::pair PressureTemperatureHelper::getMinMaxTime() const +{ + ULong64_t minTime = std::numeric_limits::max(); + ULong64_t maxTime = 0; + + if (!mPressure.first.empty()) { + minTime = std::min(minTime, mPressure.second.front()); + maxTime = std::max(maxTime, mPressure.second.back()); + } + if (!mTemperatureA.first.empty()) { + minTime = std::min(minTime, mTemperatureA.second.front()); + maxTime = std::max(maxTime, mTemperatureA.second.back()); + } + if (!mTemperatureC.first.empty()) { + minTime = std::min(minTime, mTemperatureC.second.front()); + maxTime = std::max(maxTime, mTemperatureC.second.back()); + } + + return {minTime, maxTime}; +} diff --git a/Detectors/TPC/calibration/src/SACDrawHelper.cxx b/Detectors/TPC/calibration/src/SACDrawHelper.cxx index 9779681b464b7..db5a1efee209e 100644 --- a/Detectors/TPC/calibration/src/SACDrawHelper.cxx +++ b/Detectors/TPC/calibration/src/SACDrawHelper.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. #include "TPCCalibration/SACDrawHelper.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TH2Poly.h" #include "TCanvas.h" #include "TLatex.h" diff --git a/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx b/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx index 61f0d816e1c11..316f4b25eff67 100644 --- a/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx +++ b/Detectors/TPC/calibration/src/TPCVDriftTglCalibration.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "TPCCalibration/TPCVDriftTglCalibration.h" -#include "TPCBase/ParameterGas.h" #include "Framework/Logger.h" #include "MathUtils/fit.h" #include "CommonUtils/MemFileHelper.h" @@ -91,7 +90,7 @@ void TPCVDriftTglCalibration::finalizeSlot(Slot& slot) corrFact, corrFactErr, float(cont->driftVFullMean), - cont->tOffsetRef, 0.f}); + cont->tOffsetRef, 0.f, cont->tp}); // at this stage the correction object is defined wrt average corrected drift used for the slot processing, we want to redefine it to run-constant reference vdrift vd.normalize(cont->driftVRef); diff --git a/Detectors/TPC/calibration/src/VDriftHelper.cxx b/Detectors/TPC/calibration/src/VDriftHelper.cxx index fb262acc1afa1..dc8f46af06828 100644 --- a/Detectors/TPC/calibration/src/VDriftHelper.cxx +++ b/Detectors/TPC/calibration/src/VDriftHelper.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCCalibration/VDriftHelper.h" #include "DataFormatsTPC/LtrCalibData.h" #include "TPCBase/ParameterGas.h" @@ -20,6 +20,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/InputRecord.h" #include "Framework/ConcreteDataMatcher.h" +#include "Framework/TimingInfo.h" using namespace o2::tpc; using namespace o2::framework; @@ -43,10 +44,19 @@ VDriftHelper::VDriftHelper() if (o2::conf::ConfigurableParam::getProvenance("TPCDetParam.DriftTimeOffset") == o2::conf::ConfigurableParam::EParamProvenance::kRT) { // we stick to this value mVD.creationTime = std::numeric_limits::max(); mForceParamOffset = true; - LOGP(info, "TPC dridt time offset was set from command line to {} mus ({} TB), will neglect update from CCDB", + LOGP(info, "TPC drift time offset was set from command line to {} mus ({} TB), will neglect update from CCDB", mVD.refTimeOffset, detpar.DriftTimeOffset); } + // check if temperature and pressure is set from the command line + if ((o2::conf::ConfigurableParam::getProvenance("TPCGasParam.Temperature") == o2::conf::ConfigurableParam::EParamProvenance::kRT) && (o2::conf::ConfigurableParam::getProvenance("TPCGasParam.Pressure") == o2::conf::ConfigurableParam::EParamProvenance::kRT)) { // we stick to this value + mForceTPScaling = true; + LOGP(info, "VDriftHelper: Temperature and pressure were set from command line to {} C and {} mbar, will neglect updates from CCDB", gaspar.Temperature, gaspar.Pressure); + if (gaspar.Temperature <= 0 || gaspar.Pressure <= 0) { + LOGP(info, "VDriftHelper: Disabling VDrift scaling with T / P"); + } + } + mUpdated = true; mSource = Source::Param; } @@ -74,11 +84,12 @@ void VDriftHelper::accountLaserCalibration(const LtrCalibData* calib, long fallB mVDLaser.corrFact = 1. / corr; mVDLaser.creationTime = calib->creationTime; mVDLaser.refTimeOffset = calib->refTimeOffset; + mVDLaser.refTP = calib->tp; mUpdated = true; mSource = Source::Laser; if (mMayRenormSrc & (0x1U << Source::Laser)) { // this was 1st setting? if (corr != 1.f) { // this may happen if old-style (non-normalized) standalone or non-normalized run-time laset calibration is used - LOGP(warn, "VDriftHelper: renorming initinal TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDLaser.refVDrift, mVDLaser.corrFact, mVDLaser.getVDrift(), getSourceName(mSource)); + LOGP(warn, "VDriftHelper: renorming initial TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDLaser.refVDrift, mVDLaser.corrFact, mVDLaser.getVDrift(), getSourceName(mSource)); mVDLaser.normalize(); // renorm reference to have correction = 1. } mMayRenormSrc &= ~(0x1U << Source::Laser); // unset MayRenorm @@ -103,11 +114,11 @@ void VDriftHelper::accountDriftCorrectionITSTPCTgl(const VDriftCorrFact* calib) mSource = Source::ITSTPCTgl; if (mMayRenormSrc & (0x1U << Source::ITSTPCTgl)) { // this was 1st setting? if (!mForceParamDrift && mVDTPCITSTgl.corrFact != 1.f) { // this may happen if calibration from prevous run is used - LOGP(warn, "VDriftHelper: renorming initinal TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDTPCITSTgl.refVDrift, mVDTPCITSTgl.corrFact, mVDTPCITSTgl.getVDrift(), getSourceName(mSource)); + LOGP(warn, "VDriftHelper: renorming initial TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDTPCITSTgl.refVDrift, mVDTPCITSTgl.corrFact, mVDTPCITSTgl.getVDrift(), getSourceName(mSource)); mVDTPCITSTgl.normalize(); // renorm reference to have correction = 1. } if (!mForceParamOffset && mVDTPCITSTgl.timeOffsetCorr != 0.) { - LOGP(warn, "VDriftHelper: renorming initinal TPC refTimeOffset={}/correction={} to {}/0.0, source: {}", mVDTPCITSTgl.refTimeOffset, mVDTPCITSTgl.timeOffsetCorr, mVDTPCITSTgl.getTimeOffset(), getSourceName()); + LOGP(warn, "VDriftHelper: renorming initial TPC refTimeOffset={}/correction={} to {}/0.0, source: {}", mVDTPCITSTgl.refTimeOffset, mVDTPCITSTgl.timeOffsetCorr, mVDTPCITSTgl.getTimeOffset(), getSourceName()); mVDTPCITSTgl.normalizeOffset(); } mMayRenormSrc &= ~(0x1U << Source::ITSTPCTgl); // unset MayRenorm @@ -135,9 +146,38 @@ void VDriftHelper::extractCCDBInputs(ProcessingContext& pc, bool laser, bool its if (itstpcTgl) { pc.inputs().get("vdriftTgl"); } - if (mUpdated) { // there was a change + mPTHelper.extractCCDBInputs(pc); + + if (mUpdated || mIsTPScalingPossible) { // there was a change // prefer among laser and tgl VDrift the one with the latest update time auto saveVD = mVD; + + // apply TP scaling of mVD if possible + if (float tp = mPTHelper.getTP(pc.services().get().creation); tp > 0) { + // try to extract refTP if needed + auto& vd = (mVDTPCITSTgl.creationTime < mVDLaser.creationTime) ? mVDLaser : mVDTPCITSTgl; + if (mForceTPScaling) { + const auto& gaspar = o2::tpc::ParameterGas::Instance(); + tp = (gaspar.Temperature > 0 && gaspar.Pressure > 0) ? ((gaspar.Temperature + 273.15) / gaspar.Pressure) : -1; + mIsTPScalingPossible = (tp > 0) && (vd.refTP > 0 || extractTPForVDrift(vd)); + } else { + mIsTPScalingPossible = (vd.refTP > 0) || extractTPForVDrift(vd); + } + if (mIsTPScalingPossible) { + // if no new VDrift object was loaded and if delta TP is small, do not rescale and return + if (!mUpdated && std::abs(tp - vd.refTP) < 1e-5) { + return; + } + mUpdated = true; + vd.normalize(0, tp); + if (vd.creationTime == saveVD.creationTime) { + LOGP(info, "VDriftHelper: Scaling VDrift from {} to {} with T/P from {} to {}", saveVD.getVDrift(), vd.getVDrift(), saveVD.refTP, vd.refTP); + } else { + LOGP(info, "VDriftHelper: Init new VDrift of {} with T/P {}", vd.getVDrift(), vd.refTP); + } + } + } + mVD = mVDTPCITSTgl.creationTime < mVDLaser.creationTime ? mVDLaser : mVDTPCITSTgl; auto& loserVD = mVDTPCITSTgl.creationTime < mVDLaser.creationTime ? mVDTPCITSTgl : mVDLaser; @@ -178,6 +218,8 @@ void VDriftHelper::requestCCDBInputs(std::vector& inputs, bool laser, // VDrift calibration may change during the run (in opposite to Laser calibration, at least at the moment), so ask per-TF query addInput(inputs, {"vdriftTgl", "TPC", "VDriftTgl", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalVDriftTgl), {}, 1)}); } + // adding pressure and temperature inputs + PressureTemperatureHelper::requestCCDBInputs(inputs); } //________________________________________________________ @@ -199,5 +241,51 @@ bool VDriftHelper::accountCCDBInputs(const ConcreteDataMatcher& matcher, void* o accountLaserCalibration(static_cast(obj)); return true; } - return false; + return mPTHelper.accountCCDBInputs(matcher, obj); +} + +bool VDriftHelper::extractTPForVDrift(VDriftCorrFact& vdrift, int64_t tsStepMS) +{ + const int64_t tsStart = vdrift.firstTime; + const int64_t tsEnd = vdrift.lastTime; + + if (tsStart == tsEnd) { + static bool warned = false; + if (!warned) { + warned = true; + LOGP(warn, "VDriftHelper: Cannot extract T/P for VDrift with identical start/end time {}!", tsStart); + } + return false; + } + + // make sanity check of the time range + const auto [minValidTime, maxValidTime] = mPTHelper.getMinMaxTime(); + const int64_t minTimeAccepted = static_cast(minValidTime) - 20 * o2::ccdb::CcdbObjectInfo::MINUTE; + const int64_t maxTimeAccepted = static_cast(maxValidTime) + 20 * o2::ccdb::CcdbObjectInfo::MINUTE; + + // check if the stored time stamp range is valid i.e. check if the range is in the vicinity of the current time + if ((minTimeAccepted > tsEnd) || (tsStart > maxTimeAccepted)) { + // check if creation time can be used + LOGP(warn, "VDriftHelper: Time range of VDrift object {} - {} is not valid for time range of T/P object {} - {}! Do not extract ref. T/P for VDrift!", tsStart, tsEnd, minValidTime, maxValidTime); + return false; + } + + double meanTP = 0; + int countTP = 0; + + for (int64_t ts = tsStart; ts < tsEnd; ts += tsStepMS) { + meanTP += mPTHelper.getTP(ts); + ++countTP; + } + + if (countTP == 0) { + LOGP(error, "VDriftHelper: Could not get T/P for time range {} -> {}", tsStart, tsEnd); + return false; + } + + meanTP /= countTP; + + LOGP(info, "VDriftHelper: Setting mean T/P for VDrift to {} for time range {} -> {}", meanTP, tsStart, tsEnd); + vdrift.refTP = meanTP; + return true; } diff --git a/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C b/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C index edcb69907b3e5..d488aba14e264 100644 --- a/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C +++ b/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C @@ -14,6 +14,7 @@ #include #include "TFile.h" #include "CCDB/CcdbApi.h" +#include "CommonUtils/StringUtils.h" #include "DetectorsDCS/AliasExpander.h" #include "DetectorsDCS/DeliveryType.h" #include "DetectorsDCS/DataPointIdentifier.h" @@ -24,9 +25,10 @@ #include using DPID = o2::dcs::DataPointIdentifier; +using namespace o2::utils; /// macro to populate CCDB for TPC with the configuration for DCS -int makeTPCCCDBEntryForDCS(const std::string url = "http://localhost:8080") +int makeTPCCCDBEntryForDCS(const std::string url = "http://localhost:8080", std::string comment = "") { std::unordered_map dpid2DataDesc; @@ -64,9 +66,23 @@ int makeTPCCCDBEntryForDCS(const std::string url = "http://localhost:8080") o2::ccdb::CcdbApi api; api.init(url); // or http://localhost:8080 for a local installation - std::map md; + std::map meta; + + auto toKeyValPairs = [&meta](std::vector const& tokens) { + for (auto& token : tokens) { + auto keyval = Str::tokenize(token, '=', false); + if (keyval.size() != 2) { + LOG(error) << "Illegal command-line key/value string: " << token; + continue; + } + Str::trim(keyval[1]); + meta[keyval[0]] = keyval[1]; + } + }; + toKeyValPairs(Str::tokenize(comment, ';', true)); + long ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - api.storeAsTFileAny(&dpid2DataDesc, "TPC/Config/DCSDPconfig", md, ts, 99999999999999); + api.storeAsTFileAny(&dpid2DataDesc, "TPC/Config/DCSDPconfig", meta, ts, 99999999999999); return 0; } diff --git a/Detectors/TPC/dcs/src/DCSConfigSpec.cxx b/Detectors/TPC/dcs/src/DCSConfigSpec.cxx index dc13d4ed83081..05ac93ea5e216 100644 --- a/Detectors/TPC/dcs/src/DCSConfigSpec.cxx +++ b/Detectors/TPC/dcs/src/DCSConfigSpec.cxx @@ -38,7 +38,7 @@ #include "CCDB/CcdbApi.h" #include "CommonUtils/NameConf.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/CRUCalibHelpers.h" #include "TPCBase/FEEConfig.h" #include "TPCBase/FECInfo.h" diff --git a/Detectors/TPC/dcs/src/DCSSpec.cxx b/Detectors/TPC/dcs/src/DCSSpec.cxx index 1b64ff7a75ba4..ea4e3a29ff630 100644 --- a/Detectors/TPC/dcs/src/DCSSpec.cxx +++ b/Detectors/TPC/dcs/src/DCSSpec.cxx @@ -30,7 +30,7 @@ #include "DetectorsDCS/DeliveryType.h" #include "DetectorsDCS/AliasExpander.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCdcs/DCSProcessor.h" #include "TPCdcs/DCSSpec.h" diff --git a/Detectors/TPC/monitor/src/SimpleEventDisplayGUI.cxx b/Detectors/TPC/monitor/src/SimpleEventDisplayGUI.cxx index 8784f096e3202..5509aa7473fc8 100644 --- a/Detectors/TPC/monitor/src/SimpleEventDisplayGUI.cxx +++ b/Detectors/TPC/monitor/src/SimpleEventDisplayGUI.cxx @@ -45,7 +45,7 @@ #include "TPCBase/Mapper.h" #include "TPCBase/CalDet.h" #include "TPCBase/CalArray.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "DataFormatsTPC/Constants.h" #include "TPCMonitor/SimpleEventDisplayGUI.h" diff --git a/Detectors/TPC/qc/CMakeLists.txt b/Detectors/TPC/qc/CMakeLists.txt index 6bb4c726a90fa..60195ed6d451a 100644 --- a/Detectors/TPC/qc/CMakeLists.txt +++ b/Detectors/TPC/qc/CMakeLists.txt @@ -19,6 +19,7 @@ o2_add_library(TPCQC src/SACs.cxx src/IDCsVsSACs.cxx src/TrackClusters.cxx + src/GPUErrorQA.cxx PUBLIC_LINK_LIBRARIES O2::TPCBase O2::DataFormatsTPC O2::GPUO2Interface @@ -36,7 +37,8 @@ o2_target_root_dictionary(TPCQC include/TPCQC/DCSPTemperature.h include/TPCQC/SACs.h include/TPCQC/IDCsVsSACs.h - include/TPCQC/TrackClusters.h) + include/TPCQC/TrackClusters.h + include/TPCQC/GPUErrorQA.h) o2_add_test(PID COMPONENT_NAME tpc diff --git a/Detectors/TPC/qc/include/TPCQC/GPUErrorQA.h b/Detectors/TPC/qc/include/TPCQC/GPUErrorQA.h new file mode 100644 index 0000000000000..ec171a6925a98 --- /dev/null +++ b/Detectors/TPC/qc/include/TPCQC/GPUErrorQA.h @@ -0,0 +1,69 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// @file GPUErrorQA.h +/// @author Anton Riedel, anton.riedel@cern.ch +/// + +#ifndef AliceO2_TPC_QC_GPUERRORQA_H +#define AliceO2_TPC_QC_GPUERRORQA_H + +#include +#include +#include +#include + +// root includes + +// o2 includes +// #include "DataFormatsTPC/Defs.h" + +class TH1; +namespace o2::tpc::qc +{ + +/// @brief TPC QC task for errors from GPU reconstruction +/// +/// This class is used to retrieve and visualize GPU errors +/// according to corresponding error code and location. +/// +/// origin: TPC +/// @author Anton Riedel, anton.riedel@cern.ch +class GPUErrorQA +{ + public: + /// \brief Constructor. + GPUErrorQA() = default; + + /// process gpu error reported by the reconstruction workflow + void processErrors(std::vector> errors); + + /// Initialize all histograms + void initializeHistograms(); + + /// Reset all histograms + void resetHistograms(); + + /// return histograms + const std::unordered_map>& getMapHist() const { return mMapHist; } + + /// Dump results to a file + void dumpToFile(std::string filename); + + private: + std::unordered_map> mMapHist; + + ClassDefNV(GPUErrorQA, 2); +}; +} // namespace o2::tpc::qc + +#endif // AliceO2_TPC_QC_GPUERRORQA_H diff --git a/Detectors/TPC/qc/macro/runClusters.C b/Detectors/TPC/qc/macro/runClusters.C index ea1d1b54f429e..2fd4c919be321 100644 --- a/Detectors/TPC/qc/macro/runClusters.C +++ b/Detectors/TPC/qc/macro/runClusters.C @@ -18,7 +18,7 @@ #include "SimulationDataFormat/MCTruthContainer.h" #include "DataFormatsTPC/Constants.h" #include "TPCQC/Clusters.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #endif using namespace o2::tpc; diff --git a/Detectors/TPC/qc/macro/runPID.C b/Detectors/TPC/qc/macro/runPID.C index b015ac088334b..c693189a95652 100644 --- a/Detectors/TPC/qc/macro/runPID.C +++ b/Detectors/TPC/qc/macro/runPID.C @@ -25,7 +25,7 @@ #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/TrackCuts.h" #include "TPCBase/CalDet.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TPCBase/Utils.h" #include "DataFormatsTPC/ClusterNative.h" #include "TPCQC/PID.h" diff --git a/Detectors/TPC/qc/src/Clusters.cxx b/Detectors/TPC/qc/src/Clusters.cxx index 4bf59ced195ed..dc728a10a6570 100644 --- a/Detectors/TPC/qc/src/Clusters.cxx +++ b/Detectors/TPC/qc/src/Clusters.cxx @@ -18,7 +18,7 @@ // o2 includes #include "TPCQC/Clusters.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TPCBase/ROC.h" #include "TPCBase/CRU.h" #include "TPCBase/Mapper.h" diff --git a/Detectors/TPC/qc/src/GPUErrorQA.cxx b/Detectors/TPC/qc/src/GPUErrorQA.cxx new file mode 100644 index 0000000000000..d4848aaefecb7 --- /dev/null +++ b/Detectors/TPC/qc/src/GPUErrorQA.cxx @@ -0,0 +1,80 @@ +// 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. + +#define _USE_MATH_DEFINES + +// root includes +#include "TFile.h" +#include "TH1I.h" + +// o2 includes +#include "TPCQC/GPUErrorQA.h" +#include "GPUErrors.h" + +ClassImp(o2::tpc::qc::GPUErrorQA); + +using namespace o2::tpc::qc; + +//______________________________________________________________________________ +void GPUErrorQA::initializeHistograms() +{ + TH1::AddDirectory(false); + + auto const& errorNames = o2::gpu::GPUErrors::getErrorNames(); + + int maxErrorCode = 1; + for (const auto& [key, _] : errorNames) { + if (static_cast(key) > maxErrorCode) { + maxErrorCode = key; + } + } + + // 1D histogram counting all reported errors + mMapHist["ErrorCounter"] = std::make_unique("ErrorCounter", "ErrorCounter", maxErrorCode, -0.5, maxErrorCode - 0.5); + mMapHist["ErrorCounter"]->GetXaxis()->SetTitle("Error Codes"); + mMapHist["ErrorCounter"]->GetYaxis()->SetTitle("Entries"); + // for convienence, label each bin with the error name + for (size_t bin = 1; bin <= maxErrorCode; bin++) { + auto const& it = errorNames.find(bin); + if (it != errorNames.end()) { + mMapHist["ErrorCounter"]->GetXaxis()->SetBinLabel(bin, it->second); + } else { + mMapHist["ErrorCounter"]->GetXaxis()->SetBinLabel(bin, "NO_DEF"); + } + } +} +//______________________________________________________________________________ +void GPUErrorQA::resetHistograms() +{ + for (const auto& pair : mMapHist) { + pair.second->Reset(); + } +} +//______________________________________________________________________________ +void GPUErrorQA::processErrors(std::vector> errors) +{ + for (const auto& error : errors) { + uint32_t errorCode = error[0]; + mMapHist["ErrorCounter"]->AddBinContent(errorCode); + } +} + +//______________________________________________________________________________ +void GPUErrorQA::dumpToFile(const std::string filename) +{ + auto f = std::unique_ptr(TFile::Open(filename.data(), "recreate")); + TObjArray arr; + arr.SetName("GPUErrorQA_Hists"); + for ([[maybe_unused]] const auto& [name, hist] : mMapHist) { + arr.Add(hist.get()); + } + arr.Write(arr.GetName(), TObject::kSingleKey); +} diff --git a/Detectors/TPC/qc/src/IDCsVsSACs.cxx b/Detectors/TPC/qc/src/IDCsVsSACs.cxx index 55e93f580d8a4..604a1030c3d67 100644 --- a/Detectors/TPC/qc/src/IDCsVsSACs.cxx +++ b/Detectors/TPC/qc/src/IDCsVsSACs.cxx @@ -25,7 +25,7 @@ #include "TPCCalibration/SACFactorization.h" #include "TPCBase/CalDet.h" #include "TPCBase/Mapper.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" TCanvas* o2::tpc::qc::IDCsVsSACs::drawComparisionSACandIDCZero(TCanvas* outputCanvas, int nbins1D, float xMin1D, float xMax1D, int nbins1DSAC, float xMin1DSAC, float xMax1DSAC) const { diff --git a/Detectors/TPC/qc/src/TPCQCLinkDef.h b/Detectors/TPC/qc/src/TPCQCLinkDef.h index c227ebcad8c09..3921d7dfe5649 100644 --- a/Detectors/TPC/qc/src/TPCQCLinkDef.h +++ b/Detectors/TPC/qc/src/TPCQCLinkDef.h @@ -24,6 +24,7 @@ #pragma link C++ class o2::tpc::qc::SACs + ; #pragma link C++ class o2::tpc::qc::IDCsVsSACs + ; #pragma link C++ class o2::tpc::qc::TrackClusters + ; +#pragma link C++ class o2::tpc::qc::GPUErrorQA + ; #pragma link C++ function o2::tpc::qc::helpers::makeLogBinning + ; #pragma link C++ function o2::tpc::qc::helpers::setStyleHistogram1D + ; #pragma link C++ function o2::tpc::qc::helpers::setStyleHistogram2D + ; diff --git a/Detectors/TPC/qc/src/TrackClusters.cxx b/Detectors/TPC/qc/src/TrackClusters.cxx index bcc071920e2e9..f57a35c395d58 100644 --- a/Detectors/TPC/qc/src/TrackClusters.cxx +++ b/Detectors/TPC/qc/src/TrackClusters.cxx @@ -35,16 +35,21 @@ struct binning { double max; }; +const binning binsClusters{160, 0., 160.}; const binning binsSharedClusters{160, 0., 160.}; const binning binsFoundClusters{160, 0., 160.}; const binning binsCrossedRows{160, 0., 160.}; +const binning binsRatio{150, 0., 1.5}; //______________________________________________________________________________ void TrackClusters::initializeHistograms() { TH1::AddDirectory(false); + mMapHist["clusters"].emplace_back(std::make_unique("clusters", "Clusters;NClusters;Entries", binsClusters.bins, binsClusters.min, binsClusters.max)); mMapHist["sharedClusters"].emplace_back(std::make_unique("sharedClusters", "sharedClusters;NSharedClusters;Entries", binsSharedClusters.bins, binsSharedClusters.min, binsSharedClusters.max)); - mMapHist["crossedRows"].emplace_back(std::make_unique("crossedRows", "crossedRows;crossedRows;Entries", binsCrossedRows.bins, binsCrossedRows.min, binsCrossedRows.max)); + mMapHist["crossedRows"].emplace_back(std::make_unique("crossedRows", "crossedRows;NCrossedRows;Entries", binsCrossedRows.bins, binsCrossedRows.min, binsCrossedRows.max)); + mMapHist["sharedClustersOverClusters"].emplace_back(std::make_unique("sharedClustersOverClusters", "sharedClustersOverClusters;NSharedClusters/NClusters;Entries", binsRatio.bins, binsRatio.min, binsRatio.max)); + mMapHist["clustersOverCrossedRow"].emplace_back(std::make_unique("clustersOverCrossedRow", "clustersOverCrossedRow;NClusters/NCrossedRows;Entries", binsRatio.bins, binsRatio.min, binsRatio.max)); } //______________________________________________________________________________ @@ -71,7 +76,7 @@ bool TrackClusters::processTrackAndClusters(const std::vector const auto nCls = uint8_t(track.getNClusters()); const auto eta = track.getEta(); - if (nCls < mCutMinNCls || dEdxTot < mCutMindEdxTot || abs(eta) > mCutAbsEta) { + if (nCls < mCutMinNCls || dEdxTot < mCutMindEdxTot || std::fabs(eta) > mCutAbsEta) { continue; } @@ -79,8 +84,11 @@ bool TrackClusters::processTrackAndClusters(const std::vector o2::TrackMethods::countTPCClusters(track, *clusRefs, mBufVec, *clusterIndex, shared, found, crossed); + mMapHist["clusters"][0]->Fill(found); mMapHist["sharedClusters"][0]->Fill(shared); mMapHist["crossedRows"][0]->Fill(crossed); + mMapHist["sharedClustersOverClusters"][0]->Fill(static_cast(shared) / static_cast(found)); + mMapHist["clustersOverCrossedRow"][0]->Fill(static_cast(found) / static_cast(crossed)); } return true; diff --git a/Detectors/TPC/qc/src/Tracks.cxx b/Detectors/TPC/qc/src/Tracks.cxx index 5f29e80c89d2e..dd74502540fb9 100644 --- a/Detectors/TPC/qc/src/Tracks.cxx +++ b/Detectors/TPC/qc/src/Tracks.cxx @@ -38,7 +38,7 @@ struct binning { const std::vector types{"A_Pos", "A_Neg", "C_Pos", "C_Neg"}; const binning binsDCAr{200, -5., 5.}; const binning binsDCArLargerRange{400, -10., 10.}; -const binning binsEta{200, -1., 1.}; +const binning binsEta{300, -1.5, 1.5}; const binning binsClus{120, 60., 180.}; const binning binsClusLargerRange{140, 60., 200.}; //______________________________________________________________________________ diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index ab49d0d49d79b..2c6fac7dcde2a 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -119,10 +119,10 @@ struct MergedColumnsDecoder { } // namespace detail -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::TPC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TPC, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode compressed clusters to flat buffer diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h deleted file mode 100644 index d86a845b0fe4c..0000000000000 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ClusterContainer.h -/// \brief Container class for TPC clusters -#ifndef _ALICEO2_TPC_ClusterContainer_ -#define _ALICEO2_TPC_ClusterContainer_ - -#include -#include -#include // for Float_t etc - -namespace o2 -{ -namespace tpc -{ - -/// \class ClusterContainer -/// \brief Container class for TPC clusters -class ClusterContainer -{ - public: - // Initialize the clones array - // @param clusterType Possibility to store different types of clusters - // void InitArray(const Char_t* clusterType="o2::tpc::Cluster"); - - /// Add cluster to array - /// @param output, the vector to append to - /// @param cru CRU (sector) - /// @param row Row - /// @param q Total charge of cluster - /// @param qmax Maximum charge in a single cell (pad, time) - /// @param padmean Mean position of cluster in pad direction - /// @param padsigma Sigma of cluster in pad direction - /// @param timemean Mean position of cluster in time direction - /// @param timesigma Sigma of cluster in time direction - template - static ClusterType* addCluster(std::vector* output, - Int_t cru, Int_t row, Float_t qTot, Float_t qMax, - Float_t meanpad, Float_t meantime, Float_t sigmapad, - Float_t sigmatime) - { - assert(output); - output->emplace_back(); // emplace_back a defaut constructed cluster of type ClusterType - auto& cluster = output->back(); - // set its concrete parameters: - // ATTENTION: the order of parameters in setParameters is different than in AddCluster! - cluster.setParameters(cru, row, qTot, qMax, - meanpad, sigmapad, - meantime, sigmatime); - return &cluster; - } -}; -} // namespace tpc -} // namespace o2 - -#endif diff --git a/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx b/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx index 738e6cff20df4..c6f7000089d72 100644 --- a/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx +++ b/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx @@ -19,7 +19,7 @@ #include "DataFormatsTPC/Digit.h" #include "DataFormatsTPC/ClusterNative.h" -#include "GPUO2Interface.h" +#include "GPUO2ExternalUser.h" #include "GPUO2InterfaceConfiguration.h" #include "TPCBase/Sector.h" #include "Framework/Logger.h" diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index bdf9b95e94450..5c66e4635987f 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -50,30 +50,29 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) { GPUO2Interface tracker; - float solenoidBz = -5.00668; //B-field - float refX = 1000.; //transport tracks to this x after tracking, >500 for disabling - bool continuous = false; //time frame data v.s. triggered events + float solenoidBz = -5.00668; // B-field + float refX = 1000.; // transport tracks to this x after tracking, >500 for disabling + bool continuous = false; // time frame data v.s. triggered events GPUO2InterfaceConfiguration config; - config.configDeviceBackend.deviceType = GPUDataTypes::DeviceType::CPU; + config.configDeviceBackend.deviceType = gpudatatypes::DeviceType::CPU; config.configDeviceBackend.forceDeviceType = true; - config.configProcessing.ompThreads = 4; //4 threads if we run on the CPU, 1 = default, 0 = auto-detect - config.configProcessing.runQA = false; //Run QA after tracking - config.configProcessing.eventDisplay = nullptr; //Ptr to event display backend, for running standalone OpenGL event display + config.configProcessing.ompThreads = 4; // 4 threads if we run on the CPU, 1 = default, 0 = auto-detect + config.configProcessing.runQA = false; // Run QA after tracking + config.configProcessing.eventDisplay = nullptr; // Ptr to event display backend, for running standalone OpenGL event display config.configGRP.solenoidBzNominalGPU = solenoidBz; config.configGRP.grpContinuousMaxTimeBin = continuous ? GPUSettings::TPC_MAX_TF_TIME_BIN : 0; // Number of timebins in timeframe if continuous, 0 otherwise - config.configReconstruction.tpc.nWays = 3; //Should always be 3! - config.configReconstruction.tpc.nWaysOuter = true; //Will create outer param for TRD - config.configReconstruction.tpc.searchWindowDZDR = 2.5f; //Should always be 2.5 for looper-finding and/or continuous tracking + config.configReconstruction.tpc.nWays = 3; // Should always be 3! + config.configReconstruction.tpc.searchWindowDZDR = 2.5f; // Should always be 2.5 for looper-finding and/or continuous tracking config.configReconstruction.tpc.trackReferenceX = refX; - config.configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, GPUDataTypes::RecoStep::TPCSectorTracking, - GPUDataTypes::RecoStep::TPCMerging, GPUDataTypes::RecoStep::TPCCompression, GPUDataTypes::RecoStep::TPCdEdx); - config.configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCClusters); - config.configWorkflow.outputs.set(GPUDataTypes::InOutType::TPCMergedTracks); + config.configWorkflow.steps.set(gpudatatypes::RecoStep::TPCConversion, gpudatatypes::RecoStep::TPCSectorTracking, + gpudatatypes::RecoStep::TPCMerging, gpudatatypes::RecoStep::TPCCompression, gpudatatypes::RecoStep::TPCdEdx); + config.configWorkflow.inputs.set(gpudatatypes::InOutType::TPCClusters); + config.configWorkflow.outputs.set(gpudatatypes::InOutType::TPCMergedTracks); std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); std::unique_ptr fastTransformHelper(new CorrectionMapsHelper()); diff --git a/Detectors/TPC/simulation/include/TPCSimulation/GEMAmplification.h b/Detectors/TPC/simulation/include/TPCSimulation/GEMAmplification.h index f5c40569fee43..8dbfa21febc69 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/GEMAmplification.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/GEMAmplification.h @@ -118,8 +118,10 @@ inline int GEMAmplification::getStackAmplification(const CRU& cru, const PadPos& break; } case AmplificationMode::EffectiveMode: { + const int region = static_cast(cru.gemStack()); + const float relativeGain = mGEMParam->RelativeGainStack[region]; return static_cast(static_cast(getEffectiveStackAmplification(nElectrons)) * - mGainMap->getValue(cru, pos.getRow(), pos.getPad())); + mGainMap->getValue(cru, pos.getRow(), pos.getPad()) * relativeGain); break; } } diff --git a/Detectors/TPC/simulation/macro/toyCluster.C b/Detectors/TPC/simulation/macro/toyCluster.C index d60e5a7c0f94e..7baeef1cb1a6b 100644 --- a/Detectors/TPC/simulation/macro/toyCluster.C +++ b/Detectors/TPC/simulation/macro/toyCluster.C @@ -44,7 +44,7 @@ #include "TPCBase/Mapper.h" #include "TPCBase/ParameterDetector.h" #include "TPCBase/ParameterElectronics.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCSimulation/ElectronTransport.h" #include "TPCSimulation/SAMPAProcessing.h" #include "TPCSimulation/Point.h" diff --git a/Detectors/TPC/simulation/src/DigitContainer.cxx b/Detectors/TPC/simulation/src/DigitContainer.cxx index c2e7226706eb2..dfff4b91d6451 100644 --- a/Detectors/TPC/simulation/src/DigitContainer.cxx +++ b/Detectors/TPC/simulation/src/DigitContainer.cxx @@ -17,7 +17,7 @@ #include #include #include "TPCBase/Mapper.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/ParameterElectronics.h" #include "TPCBase/IonTailSettings.h" #include "SimConfig/DigiParams.h" diff --git a/Detectors/TPC/simulation/src/Digitizer.cxx b/Detectors/TPC/simulation/src/Digitizer.cxx index cb865d9f7f752..49abc0a0b99af 100644 --- a/Detectors/TPC/simulation/src/Digitizer.cxx +++ b/Detectors/TPC/simulation/src/Digitizer.cxx @@ -24,7 +24,7 @@ #include "TPCSimulation/GEMAmplification.h" #include "TPCSimulation/Point.h" #include "TPCSimulation/SAMPAProcessing.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCSpaceCharge/SpaceCharge.h" #include "TPCBase/Mapper.h" #include "TPCCalibration/CorrMapParam.h" diff --git a/Detectors/TPC/simulation/src/ElectronTransport.cxx b/Detectors/TPC/simulation/src/ElectronTransport.cxx index f6b6f906ce862..f9e36aa642158 100644 --- a/Detectors/TPC/simulation/src/ElectronTransport.cxx +++ b/Detectors/TPC/simulation/src/ElectronTransport.cxx @@ -14,7 +14,7 @@ /// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de #include "TPCSimulation/ElectronTransport.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include diff --git a/Detectors/TPC/simulation/src/GEMAmplification.cxx b/Detectors/TPC/simulation/src/GEMAmplification.cxx index 2dc363bf151b4..8d47464e9ef53 100644 --- a/Detectors/TPC/simulation/src/GEMAmplification.cxx +++ b/Detectors/TPC/simulation/src/GEMAmplification.cxx @@ -17,7 +17,7 @@ #include #include "MathUtils/CachingTF1.h" #include -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include #include "Framework/Logger.h" #include diff --git a/Detectors/TPC/simulation/src/IDCSim.cxx b/Detectors/TPC/simulation/src/IDCSim.cxx index 45597393d8f2a..3958115d95f7c 100644 --- a/Detectors/TPC/simulation/src/IDCSim.cxx +++ b/Detectors/TPC/simulation/src/IDCSim.cxx @@ -16,7 +16,7 @@ #include "TPCBase/Mapper.h" #include #include "Framework/Logger.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "TH2Poly.h" #include "TCanvas.h" #include "TLatex.h" diff --git a/Detectors/TPC/simulation/src/SAMPAProcessing.cxx b/Detectors/TPC/simulation/src/SAMPAProcessing.cxx index 83f50832abfac..462008846fa04 100644 --- a/Detectors/TPC/simulation/src/SAMPAProcessing.cxx +++ b/Detectors/TPC/simulation/src/SAMPAProcessing.cxx @@ -14,7 +14,7 @@ /// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de #include "TPCSimulation/SAMPAProcessing.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include #include diff --git a/Detectors/TPC/simulation/test/testTPCDigitContainer.cxx b/Detectors/TPC/simulation/test/testTPCDigitContainer.cxx index 72e4dfaf6a0b2..73fca084507e5 100644 --- a/Detectors/TPC/simulation/test/testTPCDigitContainer.cxx +++ b/Detectors/TPC/simulation/test/testTPCDigitContainer.cxx @@ -23,7 +23,7 @@ #include "DataFormatsTPC/Digit.h" #include "TPCSimulation/DigitContainer.h" #include "TPCSimulation/SAMPAProcessing.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" namespace o2 { diff --git a/Detectors/TPC/simulation/test/testTPCElectronTransport.cxx b/Detectors/TPC/simulation/test/testTPCElectronTransport.cxx index 12732a52d7fa7..e42e60d5edabb 100644 --- a/Detectors/TPC/simulation/test/testTPCElectronTransport.cxx +++ b/Detectors/TPC/simulation/test/testTPCElectronTransport.cxx @@ -20,7 +20,7 @@ #include "TPCSimulation/ElectronTransport.h" #include "TPCBase/ParameterGas.h" #include "TPCBase/ParameterDetector.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TH1D.h" #include "TF1.h" diff --git a/Detectors/TPC/simulation/test/testTPCGEMAmplification.cxx b/Detectors/TPC/simulation/test/testTPCGEMAmplification.cxx index 63c092deb59c2..8a3ce711b52ef 100644 --- a/Detectors/TPC/simulation/test/testTPCGEMAmplification.cxx +++ b/Detectors/TPC/simulation/test/testTPCGEMAmplification.cxx @@ -20,7 +20,7 @@ #include "TPCSimulation/GEMAmplification.h" #include "TPCBase/ParameterGas.h" #include "TPCBase/ParameterGEM.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TH1D.h" #include "TF1.h" diff --git a/Detectors/TPC/simulation/test/testTPCSAMPAProcessing.cxx b/Detectors/TPC/simulation/test/testTPCSAMPAProcessing.cxx index a89ea335d60b5..05ed4393ea65c 100644 --- a/Detectors/TPC/simulation/test/testTPCSAMPAProcessing.cxx +++ b/Detectors/TPC/simulation/test/testTPCSAMPAProcessing.cxx @@ -18,7 +18,7 @@ #define BOOST_TEST_DYN_LINK #include #include "TPCSimulation/SAMPAProcessing.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include #include diff --git a/Detectors/TPC/simworkflow/CMakeLists.txt b/Detectors/TPC/simworkflow/CMakeLists.txt new file mode 100644 index 0000000000000..e442d45fab63f --- /dev/null +++ b/Detectors/TPC/simworkflow/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(TPCSimWorkflow + SOURCES + src/ChunkedDigitPublisher.cxx + src/TPCDigitRootWriterSpec.cxx + PUBLIC_LINK_LIBRARIES O2::TPCSimulation O2::Framework) + +o2_add_executable(chunkeddigit-merger + COMPONENT_NAME tpc + TARGETVARNAME mergertargetName + SOURCES src/ChunkedDigitPublisher.cxx + PUBLIC_LINK_LIBRARIES O2::TPCSimWorkflow) + +if(OpenMP_CXX_FOUND) + # Must be private, depending libraries might be compiled by compiler not understanding -fopenmp + target_compile_definitions(${mergertargetName} PRIVATE WITH_OPENMP) + target_link_libraries(${mergertargetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.h b/Detectors/TPC/simworkflow/include/TPCSimWorkflow/TPCDigitRootWriterSpec.h similarity index 100% rename from Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.h rename to Detectors/TPC/simworkflow/include/TPCSimWorkflow/TPCDigitRootWriterSpec.h diff --git a/Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx b/Detectors/TPC/simworkflow/src/ChunkedDigitPublisher.cxx similarity index 75% rename from Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx rename to Detectors/TPC/simworkflow/src/ChunkedDigitPublisher.cxx index adf0cba944c03..bdc2f358a4169 100644 --- a/Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx +++ b/Detectors/TPC/simworkflow/src/ChunkedDigitPublisher.cxx @@ -19,6 +19,7 @@ #include "Framework/DataAllocator.h" #include "Framework/ControlService.h" #include "DataFormatsTPC/Digit.h" +#include "TPCSimWorkflow/TPCDigitRootWriterSpec.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "TPCSimulation/CommonMode.h" @@ -46,6 +47,9 @@ #include #endif #include +#include "CommonDataFormat/RangeReference.h" + +using DigiGroupRef = o2::dataformats::RangeReference; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; @@ -71,6 +75,9 @@ void customize(std::vector& workflowOptions) workflowOptions.push_back( ConfigParamSpec{"tpc-sectors", VariantType::String, sectorDefault.c_str(), {sectorshelp}}); + // option to write merged data to file + workflowOptions.push_back(ConfigParamSpec{"writer-mode", o2::framework::VariantType::Bool, false, {"enable ROOT file output"}}); + // option to disable MC truth workflowOptions.push_back(ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable mc-truth"}}); workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}); @@ -104,14 +111,30 @@ void copyHelper(MCTruthContainer const& origin, MCTruthContain target.mergeAtBack(origin); } +// a trait to map TPC data types to a DPL channel name +template +struct OutputChannelName; +template <> +struct OutputChannelName> { + static constexpr char value[] = "DIGITS"; +}; +template <> +struct OutputChannelName> { + static constexpr char value[] = "COMMONMODE"; +}; +template <> +struct OutputChannelName> { + static constexpr char value[] = "DIGTRIGGERS"; +}; + template auto makePublishBuffer(framework::ProcessingContext& pc, int sector, uint64_t activeSectors) { - LOG(info) << "PUBLISHING SECTOR " << sector; + LOG(info) << "PUBLISHING SECTOR " << sector << " FOR CHANNEL " << OutputChannelName::value; o2::tpc::TPCSectorHeader header{sector}; header.activeSectors = activeSectors; - return &pc.outputs().make(Output{"TPC", "DIGITS", static_cast(sector), header}); + return &pc.outputs().make(Output{"TPC", OutputChannelName::value, static_cast(sector), header}); } template <> @@ -187,6 +210,30 @@ void mergeHelper(const char* brprefix, std::vector const& tpcsectors, uint6 } } +template <> +void mergeHelper>(const char* brprefix, std::vector const& tpcsectors, uint64_t activeSectors, + TFile& originfile, framework::ProcessingContext& pc) +{ + // specialization for TPC Trigger + auto keyslist = originfile.GetListOfKeys(); + for (int i = 0; i < keyslist->GetEntries(); ++i) { + auto key = keyslist->At(i); + int sector = atoi(key->GetName()); + if (std::find(tpcsectors.begin(), tpcsectors.end(), sector) == tpcsectors.end()) { + // do nothing if sector not wanted + continue; + } + + using AccumType = std::decay_t>(pc, sector, activeSectors))>; + AccumType accum; +#pragma omp critical + accum = makePublishBuffer>(pc, sector, activeSectors); + // no actual data sent. Continuous mode. + + publishBuffer(pc, sector, activeSectors, accum); + } +} + void publishMergedTimeframes(std::vector const& lanes, std::vector const& tpcsectors, bool domctruth, framework::ProcessingContext& pc) { uint64_t activeSectors = 0; @@ -208,13 +255,21 @@ void publishMergedTimeframes(std::vector const& lanes, std::vector con auto originfile = new TFile(filename.c_str(), "OPEN"); assert(originfile); - //data definitions + // data definitions using DigitsType = std::vector; using LabelType = o2::dataformats::MCTruthContainer; mergeHelper("TPCDigit_", tpcsectors, activeSectors, *originfile, pc); if (domctruth) { mergeHelper("TPCDigitMCTruth_", tpcsectors, activeSectors, *originfile, pc); } + + // we also merge common modes and publish a (fake) trigger entry + using CommonModeType = std::vector; + mergeHelper("TPCCommonMode_", tpcsectors, activeSectors, *originfile, pc); + + using TriggerType = std::vector; + mergeHelper("TPCCommonMode_", tpcsectors, activeSectors, *originfile, pc); + originfile->Close(); delete originfile; } @@ -257,7 +312,7 @@ class Task /// MC truth information is also aggregated and written out DataProcessorSpec getSpec(std::vector const& laneConfiguration, std::vector const& tpcsectors, bool mctruth, bool publish = true) { - //data definitions + // data definitions using DigitsOutputType = std::vector; using CommonModeOutputType = std::vector; @@ -266,10 +321,14 @@ DataProcessorSpec getSpec(std::vector const& laneConfiguration, std::vector // effectively the input expects one sector per subspecification for (int s = 0; s < 36; ++s) { OutputLabel binding{std::to_string(s)}; - outputs.emplace_back(/*binding,*/ "TPC", "DIGITS", static_cast(s), Lifetime::Timeframe); + outputs.emplace_back("TPC", "DIGITS", static_cast(s), Lifetime::Timeframe); if (mctruth) { - outputs.emplace_back(/*binding,*/ "TPC", "DIGITSMCTR", static_cast(s), Lifetime::Timeframe); + outputs.emplace_back("TPC", "DIGITSMCTR", static_cast(s), Lifetime::Timeframe); } + // common mode + outputs.emplace_back("TPC", "COMMONMODE", static_cast(s), Lifetime::Timeframe); + // trigger records + outputs.emplace_back("TPC", "DIGTRIGGERS", static_cast(s), Lifetime::Timeframe); } } @@ -287,12 +346,25 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto numlanes = configcontext.options().get("tpc-lanes"); bool mctruth = !configcontext.options().get("disable-mc"); + bool writeout = configcontext.options().get("writer-mode"); auto tpcsectors = o2::RangeTokenizer::tokenize(configcontext.options().get("tpc-sectors")); std::vector lanes(numlanes); std::iota(lanes.begin(), lanes.end(), 0); specs.emplace_back(o2::tpc::getSpec(lanes, tpcsectors, mctruth)); + if (writeout) { + // for now writeout to a ROOT file only works if all sectors + // are included + if (tpcsectors.size() != 36) { + LOG(error) << "You currently need to include all TPC sectors in the ROOT writer-mode"; + } else { + std::vector writerlanes(tpcsectors.size()); + std::iota(writerlanes.begin(), writerlanes.end(), 0); + specs.emplace_back(o2::tpc::getTPCDigitRootWriterSpec(writerlanes, mctruth)); + } + } + // 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/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx b/Detectors/TPC/simworkflow/src/TPCDigitRootWriterSpec.cxx similarity index 95% rename from Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx rename to Detectors/TPC/simworkflow/src/TPCDigitRootWriterSpec.cxx index 9bc9b9ba45e71..a907a73281884 100644 --- a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx +++ b/Detectors/TPC/simworkflow/src/TPCDigitRootWriterSpec.cxx @@ -14,7 +14,7 @@ /// @since 2018-04-19 /// @brief Processor spec for a ROOT file writer for TPC digits -#include "TPCDigitRootWriterSpec.h" +#include "TPCSimWorkflow/TPCDigitRootWriterSpec.h" #include "DataFormatsTPC/TPCSectorHeader.h" #include "CommonDataFormat/RangeReference.h" #include "Framework/InputRecord.h" @@ -77,7 +77,7 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector const& laneConfigur } }; - //branch definitions for RootTreeWriter spec + // branch definitions for RootTreeWriter spec using DigitsOutputType = std::vector; using CommonModeOutputType = std::vector; @@ -156,8 +156,8 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector const& laneConfigur LOG(info) << "DIGIT SIZE " << digiData.size(); const auto& trigS = (*trigP2Sect.get())[sector]; int entries = 0; - if (!trigS.size()) { - std::runtime_error("Digits for sector " + std::to_string(sector) + " are received w/o info on grouping in triggers"); + if (trigS.size() == 0) { + LOG(warn) << "Digits for sector " + std::to_string(sector) + " are received w/o trigger info. Will assume continuous mode"; } else { // check consistency of Ndigits with that of expected from the trigger int nExp = trigS.back().getFirstEntry() + trigS.back().getEntries() - trigS.front().getFirstEntry(); if (nExp != digiData.size()) { @@ -167,7 +167,7 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector const& laneConfigur } { - if (trigS.size() == 1) { // just 1 entry (continous mode?), use digits directly + if (trigS.size() <= 1) { // just 1 entry (continous mode?), use digits directly auto ptr = &digiData; branch.SetAddress(&ptr); branch.Fill(); @@ -214,8 +214,8 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector const& laneConfigur LOG(info) << "MCTRUTH ELEMENTS " << labeldata.getIndexedSize() << " WITH " << labeldata.getNElements() << " LABELS"; const auto& trigS = (*trigP2Sect.get())[sector]; - if (!trigS.size()) { - throw std::runtime_error("MCTruth for sector " + std::to_string(sector) + " are received w/o info on grouping in triggers"); + if (trigS.size() == 0) { + LOG(warn) << "MCTruth for sector " + std::to_string(sector) + " received w/o trigger info. Will assume continuous mode"; } else { int nExp = trigS.back().getFirstEntry() + trigS.back().getEntries() - trigS.front().getFirstEntry(); if (nExp != labeldata.getIndexedSize()) { @@ -225,7 +225,7 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector const& laneConfigur } } { - if (trigS.size() == 1) { // just 1 entry (continous mode?), use labels directly + if (trigS.size() <= 1) { // just 0 or 1 entry (continous mode?), use labels directly outputcontainer.adopt(labelbuffer); br->Fill(); br->ResetAddress(); diff --git a/Detectors/TPC/spacecharge/CMakeLists.txt b/Detectors/TPC/spacecharge/CMakeLists.txt index a2f4cdb51becb..390e6c99c9c7e 100644 --- a/Detectors/TPC/spacecharge/CMakeLists.txt +++ b/Detectors/TPC/spacecharge/CMakeLists.txt @@ -15,7 +15,7 @@ o2_add_library(TPCSpaceCharge src/PoissonSolver.cxx src/TriCubic.cxx src/DataContainer3D.cxx - PUBLIC_LINK_LIBRARIES O2::TPCBase + PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim O2::Field Vc::Vc ROOT::Core diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h index 79400c3d3d214..73638c5b2982f 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h @@ -186,6 +186,9 @@ struct DataContainer3D { /// print the matrix void print() const; + /// convert a data container to a new datacontainer with different grid definition (e.g. different number of vertices) + DataContainer3D convert(const o2::tpc::RegularGrid3D& gridNew, const o2::tpc::RegularGrid3D& gridRef, const int threads = 1) const; + /// operator overload DataContainer3D& operator*=(const DataT value); DataContainer3D& operator+=(const DataContainer3D& other); diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h index 218bddcf49402..933273d0b5eb9 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h @@ -19,6 +19,7 @@ #define ALICEO2_TPC_POISSONSOLVERHELPERS_H_ #include "CommonConstants/MathConstants.h" +#include "DataFormatsTPC/Defs.h" namespace o2 { @@ -55,7 +56,7 @@ struct MGParameters { ///< Parameter inline static int nMGCycle = 200; ///< number of multi grid cycle (V type) inline static int maxLoop = 7; ///< the number of tree-deep of multi grid inline static int gamma = 1; ///< number of iteration at coarsest level !TODO SET TO REASONABLE VALUE! - inline static bool normalizeGridToOneSector = false; ///< the grid in phi direction is squashed from 2 Pi to (2 Pi / SECTORSPERSIDE). This can used to get the potential for phi symmetric sc density or boundary potentials + inline static int normalizeGridToNSector = SECTORSPERSIDE; ///< the grid in phi direction is squashed from 2 Pi to (2 Pi / SECTORSPERSIDE). This can used to get the potential for phi symmetric sc density or boundary potentials }; template diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h index ad76ec2b6da5b..d0c66d0ff3df1 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h @@ -204,10 +204,13 @@ class SpaceCharge /// simulate only one sector instead of 18 per side. This makes currently only sense for the static distortions (ToDo: simplify usage) /// phi max will be restricted to 2Pi/18 for this instance and for global instance of poisson solver - void setSimOneSector(); + void setSimOneSector() { setSimNSector(1); } + + /// simulate N sectors + void setSimNSector(const int nSectors); /// unsetting simulation of one sector - static void unsetSimOneSector(); + static void unsetSimNSector(); /// setting default potential (same potential for all GEM frames. The default value of 1000V are matched to distortions observed in laser data without X-Ray etc. /// \param side side of the TPC where the potential will be set @@ -308,10 +311,24 @@ class SpaceCharge /// scaling the space-charge density for given stack void scaleChargeDensityStack(const float scalingFactor, const Sector sector, const GEMstack stack); + /// scale the potential by a scaling factor + /// \param scalingFactor factor to scale the potential + /// \param side side for which the potential will be scaled + void scalePotential(const DataT scalingFactor, const Side side) { mPotential[side] *= scalingFactor; } + /// add space charge density from other object (this.mDensity = this.mDensity + other.mDensity) /// \param otherSC other space-charge object, which charge will be added to current object void addChargeDensity(const SpaceCharge& otherSC); + /// add global corrections from other space charge object + void addGlobalCorrections(const SpaceCharge& otherSC, const Side side); + + /// convert space-charge object to new definition of number of vertices + /// \param nZNew new number of vertices in z direction + /// \param nRNew new number of vertices in r direction + /// \param nPhiNew new number of vertices in phi direction + void downSampleObject(const int nZNew, const int nRNew, const int nPhiNew); + /// step 3: calculate the local distortions and corrections with an electric field /// \param type calculate local corrections or local distortions: type = o2::tpc::SpaceCharge<>::Type::Distortions or o2::tpc::SpaceCharge<>::Type::Corrections /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi (analytical formula or by TriCubic interpolator) @@ -415,6 +432,9 @@ class SpaceCharge /// \param phi global phi coordinate DataT getDensityCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + /// get the potential for list of given coordinate + std::vector getDensityCyl(const std::vector& z, const std::vector& r, const std::vector& phi, const Side side) const; + /// get the potential for given coordinate /// \param z global z coordinate /// \param r global r coordinate @@ -1184,6 +1204,10 @@ class SpaceCharge /// \param gCorr function returning global corrections for given global coordinate void setGlobalCorrections(const std::function& gCorr, const Side side); + /// setting the global distortions directly from input function provided in global coordinates + /// \param gDist function returning global distortions for given global coordinate + void setGlobalDistortions(const std::function& gDist, const Side side); + /// set misalignment of ROC for shift in z /// \param sector sector for which the misalignment in z will be applied (if sector=-1 all sectors are shifted) /// \param type 0=IROC, 1=OROC, 2=IROC+OROC @@ -1229,7 +1253,16 @@ class SpaceCharge /// \param tgl tgl of the track /// \param nPoints number of points used to calculate the DCAr /// \param pcstream if provided debug output is being created - float getDCAr(float tgl, const int nPoints, const float phi, o2::utils::TreeStreamRedirector* pcstream = nullptr) const; + float getDCAr(float tgl, const int nPoints, const float phi, float rStart = -1, o2::utils::TreeStreamRedirector* pcstream = nullptr) const; + + /// \return returns nearest phi vertex for given phi position + size_t getNearestPhiVertex(const DataT phi, const Side side) const { return std::round(phi / getGridSpacingPhi(side)); } + + /// \return returns nearest r vertex for given radius position + size_t getNearestRVertex(const DataT r, const Side side) const { return std::round((r - getRMin(side)) / getGridSpacingR(side) + 0.5); } + + /// \return returns number of bins in phi direction for the gap between sectors and for the GEM frame + size_t getPhiBinsGapFrame(const Side side) const; private: ParamSpaceCharge mParamGrid{}; ///< parameters of the grid on which the calculations are performed @@ -1352,15 +1385,6 @@ class SpaceCharge /// dump the created electron tracks with calculateElectronDriftPath function to a tree void dumpElectronTracksToTree(const std::vector>, std::array>>& electronTracks, const int nSamplingPoints, const char* outFile) const; - /// \return returns nearest phi vertex for given phi position - size_t getNearestPhiVertex(const DataT phi, const Side side) const { return std::round(phi / getGridSpacingPhi(side)); } - - /// \return returns nearest r vertex for given radius position - size_t getNearestRVertex(const DataT r, const Side side) const { return std::round((r - getRMin(side)) / getGridSpacingR(side) + 0.5); } - - /// \return returns number of bins in phi direction for the gap between sectors and for the GEM frame - size_t getPhiBinsGapFrame(const Side side) const; - /// \return setting the boundary potential for given GEM stack void setPotentialBoundaryGEMFrameAlongPhi(const std::function& potentialFunc, const GEMstack stack, const bool bottom, const Side side, const bool outerFrame = false); @@ -1372,16 +1396,16 @@ class SpaceCharge void initAllBuffers(); - void setBoundaryFromIndices(const std::function& potentialFunc, const std::vector& indices, const Side side); + void setBoundaryFromIndices(const std::function& potentialFunc, const std::vector>& indices, const Side side); /// get indices of the GEM frame along r - std::vector getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const; + std::vector> getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const; /// get indices of the GEM frame along phi - std::vector getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap = false) const; + std::vector> getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap = false) const; void setROCMisalignment(int stackType, int misalignmentType, int sector, const float potMin, const float potMax); - void fillROCMisalignment(const std::vector& indicesTop, const std::vector& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar); + void fillROCMisalignment(const std::vector>& indicesTop, const std::vector>& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar); /// set potentialsdue to ROD misalignment void initRodAlignmentVoltages(const MisalignmentType misalignmentType, const FCType fcType, const int sector, const Side side, const float deltaPot); @@ -1389,6 +1413,8 @@ class SpaceCharge void calcGlobalDistCorrIterative(const DistCorrInterpolator& globCorr, const int maxIter, const DataT approachZ, const DataT approachR, const DataT approachPhi, const DataT diffCorr, const SpaceCharge* scSCale, float scale, const Type type); void calcGlobalDistCorrIterativeLinearCartesian(const DistCorrInterpolator& globCorr, const int maxIter, const DataT approachX, const DataT approachY, const DataT approachZ, const DataT diffCorr, const SpaceCharge* scSCale, float scale, const Type type); + void setGlobalDistCorr(const Type type, const std::function& gFunc, const Side side); + ClassDefNV(SpaceCharge, 6); }; diff --git a/Detectors/TPC/spacecharge/macro/createSCHistosFromHits.C b/Detectors/TPC/spacecharge/macro/createSCHistosFromHits.C index f6232018f3c59..cf4e5b2719b22 100644 --- a/Detectors/TPC/spacecharge/macro/createSCHistosFromHits.C +++ b/Detectors/TPC/spacecharge/macro/createSCHistosFromHits.C @@ -112,7 +112,7 @@ g++ -o createSCHistosFromHits createSCHistosFromHits.C -I ~/alice/sw/osx_x86-64/ #include "TPCBase/ParameterDetector.h" #include "TPCBase/ParameterGEM.h" #include "TPCBase/ParameterElectronics.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCSimulation/ElectronTransport.h" #include "TPCSimulation/GEMAmplification.h" #include "TPCSimulation/SAMPAProcessing.h" diff --git a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx index c9a39e940873d..60d7c28b8c74e 100644 --- a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx +++ b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx @@ -17,6 +17,8 @@ #include "Framework/Logger.h" #include "TFile.h" #include "ROOT/RDataFrame.hxx" +#include "TStopwatch.h" +#include "TTree.h" #include #include @@ -329,7 +331,6 @@ void DataContainer3D::dumpSlice(std::string_view treename, std::string_vi ROOT::RDataFrame dFrame(treename, fileIn); auto df = dFrame.Define("slice", [rangeiZ, rangeiR, rangeiPhi](const std::pair>& values, unsigned short nz, unsigned short nr, unsigned short nphi) { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; std::vector ir; std::vector iphi; std::vector iz; @@ -368,12 +369,12 @@ void DataContainer3D::dumpSlice(std::string_view treename, std::string_vi const float rTmp = o2::tpc::GridProperties::getRMin() + o2::tpc::GridProperties::getGridSpacingR(nr) * iRTmp; const float zTmp = o2::tpc::GridProperties::getZMin() + o2::tpc::GridProperties::getGridSpacingZ(nz) * iZTmp; - const float phiTmp = o2::tpc::GridProperties::getPhiMin() + o2::tpc::GridProperties::getGridSpacingPhi(nphi) / (simOneSectorOnly ? SECTORSPERSIDE : 1) * iPhiTmp; + const float phiTmp = o2::tpc::GridProperties::getPhiMin() + o2::tpc::GridProperties::getGridSpacingPhi(nphi) * (MGParameters::normalizeGridToNSector / double(SECTORSPERSIDE)) * iPhiTmp; const float x = rTmp * std::cos(phiTmp); const float y = rTmp * std::sin(phiTmp); const LocalPosition3D pos(x, y, zTmp); - unsigned char secNum = simOneSectorOnly ? 0 : std::floor(phiTmp / SECPHIWIDTH); + unsigned char secNum = std::floor(phiTmp / SECPHIWIDTH); Sector sector(secNum + (pos.Z() < 0) * SECTORSPERSIDE); LocalPosition3D lPosTmp = Mapper::GlobalToLocal(pos, sector); @@ -426,10 +427,9 @@ void DataContainer3D::dumpInterpolation(std::string_view treename, std::s // define grid for interpolation using GridProp = GridProperties; - const RegularGrid3D mGrid3D(GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::getGridSpacingZ(nz), GridProp::getGridSpacingR(nr), o2::tpc::GridProperties::getGridSpacingPhi(nphi) / (MGParameters::normalizeGridToOneSector ? SECTORSPERSIDE : 1), ParamSpaceCharge{nr, nz, nphi}); + const RegularGrid3D mGrid3D(GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::getGridSpacingZ(nz), GridProp::getGridSpacingR(nr), o2::tpc::GridProperties::getGridSpacingPhi(nphi) * (MGParameters::normalizeGridToNSector / double(SECTORSPERSIDE)), ParamSpaceCharge{nr, nz, nphi}); auto interpolate = [&mGrid3D = std::as_const(mGrid3D), &data = std::as_const(data), rangeR, rangeZ, rangePhi, nR, nZ, nPhi](unsigned int, ULong64_t iPhi) { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; std::vector ir; std::vector iphi; std::vector iz; @@ -471,7 +471,7 @@ void DataContainer3D::dumpInterpolation(std::string_view treename, std::s const float x = rPos * std::cos(phiPos); const float y = rPos * std::sin(phiPos); const LocalPosition3D pos(x, y, zPos); - unsigned char secNum = simOneSectorOnly ? 0 : std::floor(phiPos / SECPHIWIDTH); + unsigned char secNum = std::floor(phiPos / SECPHIWIDTH); // TODO CHECK THIS Sector sector(secNum + (pos.Z() < 0) * SECTORSPERSIDE); LocalPosition3D lPosTmp = Mapper::GlobalToLocal(pos, sector); lPos.emplace_back(lPosTmp); @@ -510,6 +510,27 @@ bool DataContainer3D::getVertices(std::string_view treename, std::string_ return true; } +template +DataContainer3D DataContainer3D::convert(const o2::tpc::RegularGrid3D& gridNew, const o2::tpc::RegularGrid3D& gridRef, const int threads) const +{ + const int nZNew = gridNew.getNZ(); + const int nRNew = gridNew.getNR(); + const int nPhiNew = gridNew.getNPhi(); + DataContainer3D contCont(nZNew, nRNew, nPhiNew); +#pragma omp parallel for num_threads(threads) + for (size_t iPhi = 0; iPhi < nPhiNew; ++iPhi) { + const DataT phi = gridNew.getPhiVertex(iPhi); + for (size_t iR = 0; iR < nRNew; ++iR) { + const DataT radius = gridNew.getRVertex(iR); + for (size_t iZ = 0; iZ < nZNew; ++iZ) { + const DataT z = gridNew.getZVertex(iZ); + contCont(iZ, iR, iPhi) = interpolate(z, radius, phi, gridRef); + } + } + } + return contCont; +} + template class o2::tpc::DataContainer3D; template class o2::tpc::DataContainer3D; diff --git a/Detectors/TPC/spacecharge/src/PoissonSolver.cxx b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx index 952f4b29111ce..d00e268f64125 100644 --- a/Detectors/TPC/spacecharge/src/PoissonSolver.cxx +++ b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx @@ -940,7 +940,7 @@ void PoissonSolver::residue2D(Vector& residue, const Vector& matricesCurr for (int j = 1; j < tnZColumn - 1; ++j) { residue(i, j, iPhi) = ih2 * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) - inverseTempFourth * matricesCurrentV(i, j, iPhi)) + matricesCurrentCharge(i, j, iPhi); } // end cols - } // end nRRow + } // end nRRow // Boundary points. for (int i = 0; i < tnRRow; ++i) { @@ -997,7 +997,7 @@ void PoissonSolver::residue3D(Vector& residue, const Vector& matricesCurr coefficient3[i] * (signPlus * matricesCurrentV(i, j, mp1) + signMinus * matricesCurrentV(i, j, mm1)) - inverseCoefficient4[i] * matricesCurrentV(i, j, m)) + matricesCurrentCharge(i, j, m); } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices } } @@ -1263,9 +1263,9 @@ void PoissonSolver::relax3D(Vector& matricesCurrentV, const Vector& matri for (int i = isw; i < tnRRow - 1; i += 2) { (matricesCurrentV)(i, j, m) = (coefficient2[i] * (matricesCurrentV)(i - 1, j, m) + tempRatioZ * ((matricesCurrentV)(i, j - 1, m) + (matricesCurrentV)(i, j + 1, m)) + coefficient1[i] * (matricesCurrentV)(i + 1, j, m) + coefficient3[i] * (signPlus * (matricesCurrentV)(i, j, mp1) + signMinus * (matricesCurrentV)(i, j, mm1)) + (h2 * (matricesCurrentCharge)(i, j, m))) * coefficient4[i]; } // end cols - } // end mParamGrid.NRVertices - } // end phi - } // end sweep + } // end mParamGrid.NRVertices + } // end phi + } // end sweep } else if (MGParameters::relaxType == RelaxType::Jacobi) { // for each slice for (int m = 0; m < iPhi; ++m) { @@ -1306,8 +1306,8 @@ void PoissonSolver::relax3D(Vector& matricesCurrentV, const Vector& matri for (int i = 1; i < tnRRow - 1; ++i) { (matricesCurrentV)(i, j, m) = (coefficient2[i] * (matricesCurrentV)(i - 1, j, m) + tempRatioZ * ((matricesCurrentV)(i, j - 1, m) + (matricesCurrentV)(i, j + 1, m)) + coefficient1[i] * (matricesCurrentV)(i + 1, j, m) + coefficient3[i] * (signPlus * (matricesCurrentV)(i, j, mp1) + signMinus * (matricesCurrentV)(i, j, mm1)) + (h2 * (matricesCurrentCharge)(i, j, m))) * coefficient4[i]; } // end cols - } // end mParamGrid.NRVertices - } // end phi + } // end mParamGrid.NRVertices + } // end phi } else { // Case weighted Jacobi // TODO @@ -1329,15 +1329,15 @@ void PoissonSolver::relax2D(Vector& matricesCurrentV, const Vector& matri matricesCurrentV(i, j, iPhi) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) + (h2 * matricesCurrentCharge(i, j, iPhi))); } // end cols - } // end mParamGrid.NRVertices - } // end pass red-black + } // end mParamGrid.NRVertices + } // end pass red-black } else if (MGParameters::relaxType == RelaxType::Jacobi) { for (int j = 1; j < tnZColumn - 1; ++j) { for (int i = 1; i < tnRRow - 1; ++i) { matricesCurrentV(i, j, iPhi) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) + (h2 * matricesCurrentCharge(i, j, iPhi))); } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices } else if (MGParameters::relaxType == RelaxType::WeightedJacobi) { // Weighted Jacobi // TODO @@ -1421,7 +1421,7 @@ void PoissonSolver::restrict3D(Vector& matricesCurrentCharge, const Vecto matricesCurrentCharge(i, j, m) = residue(ii, jj, mm) / 8 + s1 / 16 + s2 / 32 + s3 / 64; } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices // for boundary for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { @@ -1460,7 +1460,7 @@ void PoissonSolver::restrict2D(Vector& matricesCurrentCharge, const Vecto (residue(iip1, jjp1, iphi) + residue(iim1, jjp1, iphi) + residue(iip1, jjm1, iphi) + residue(iim1, jjm1, iphi)) / 16; } } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices // boundary // for boundary for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { @@ -1520,7 +1520,7 @@ void PoissonSolver::calcCoefficients2D(unsigned int from, unsigned int to template DataT PoissonSolver::getGridSizePhiInv() { - return MGParameters::normalizeGridToOneSector ? (INVTWOPI * SECTORSPERSIDE) : INVTWOPI; + return INVTWOPI * SECTORSPERSIDE / MGParameters::normalizeGridToNSector; } template class o2::tpc::PoissonSolver; diff --git a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx index 07101bac15c23..b80d2a7606ee7 100644 --- a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx +++ b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx @@ -25,7 +25,7 @@ #include "Field/MagneticField.h" #include "CommonUtils/TreeStreamRedirector.h" #include "TPCBase/CalDet.h" -#include "TPCBase/Painter.h" +#include "TPCBaseRecSim/Painter.h" #include "MathUtils/Utils.h" #include "DataFormatsParameters/GRPMagField.h" #include "GPUDebugStreamer.h" @@ -43,6 +43,7 @@ #include "TStopwatch.h" #include "ROOT/RDataFrame.hxx" #include "THnSparse.h" +#include "TRandom.h" #include @@ -246,6 +247,10 @@ void SpaceCharge::setDefaultStaticDistortionsGEMFrameChargeUp(const Side template size_t SpaceCharge::getPhiBinsGapFrame(const Side side) const { + if (MGParameters::normalizeGridToNSector == 1) { + return 0; + } + const auto& regInf = Mapper::instance().getPadRegionInfo(0); const float localYEdgeIROC = regInf.getPadsInRowRegion(0) / 2 * regInf.getPadWidth(); const auto globalPosGap = Mapper::LocalToGlobal(LocalPosition2D(regInf.getRadiusFirstRow(), -(localYEdgeIROC + GEMFrameParameters::WIDTHFRAME)), Sector(0)); @@ -268,16 +273,15 @@ void SpaceCharge::setPotentialBoundaryGEMFrameAlongR(const std::function< } template -std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const +std::vector> SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; const auto radiusStart = std::sqrt(std::pow(GEMFrameParameters::LENGTHFRAMEIROCBOTTOM / 2, 2) + std::pow(GEMFrameParameters::POSBOTTOM[0], 2)); const auto rStart = getNearestRVertex(radiusStart, side); const auto radiusEnd = std::sqrt(std::pow(GEMFrameParameters::LENGTHFRAMEOROC3TOP / 2, 2) + std::pow(GEMFrameParameters::POSTOP[3], 2)); const auto rEnd = getNearestRVertex(radiusEnd, side); // mParamGrid.NRVertices - 1 - const int verticesPerSector = simOneSectorOnly ? mParamGrid.NPhiVertices : mParamGrid.NPhiVertices / SECTORSPERSIDE; + const int verticesPerSector = mParamGrid.NPhiVertices / MGParameters::normalizeGridToNSector; const auto& regInf = Mapper::instance().getPadRegionInfo(0); const float localYEdgeIROC = regInf.getPadsInRowRegion(0) / 2 * regInf.getPadWidth(); @@ -300,7 +304,8 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndice radii.emplace_back(std::sqrt(std::pow(GEMFrameParameters::POSTOP[stack], 2) + std::pow(localYEdge, 2))); } - std::vector potentialInd; + std::vector> potentialInd; + const float weight = 1; for (size_t iR = rStart; iR < rEnd; ++iR) { const DataT radius = getRVertex(iR, side); auto const it = std::lower_bound(radii.begin(), radii.end(), radius); @@ -315,13 +320,13 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndice break; } - for (int sector = 0; sector < (simOneSectorOnly ? 1 : SECTORSPERSIDE); ++sector) { + for (int sector = 0; sector < MGParameters::normalizeGridToNSector; ++sector) { const size_t iPhiLeft = sector * verticesPerSector + iPhiTmp; const size_t iZ = mParamGrid.NZVertices - 1; - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft)); + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft), weight); if (iPhiTmp > 0) { const size_t iPhiRight = (sector + 1) * verticesPerSector - iPhiTmp; - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight)); + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight), weight); } } } @@ -339,37 +344,35 @@ void SpaceCharge::setPotentialBoundaryGEMFrameAlongPhi(const std::functio } template -void SpaceCharge::setBoundaryFromIndices(const std::function& potentialFunc, const std::vector& indices, const Side side) +void SpaceCharge::setBoundaryFromIndices(const std::function& potentialFunc, const std::vector>& indices, const Side side) { - for (const auto& index : indices) { + /* + make check for the weights + Loop over bins in the radial direction + Check for duplicates and use the one with larger weight + */ + + for (const auto& indexw : indices) { + const int index = indexw.first; const int iZ = mPotential[side].getIndexZ(index); const int iR = mPotential[side].getIndexR(index); const int iPhi = mPotential[side].getIndexPhi(index); const DataT radius = getRVertex(iR, side); - mPotential[side](iZ, iR, iPhi) = potentialFunc(radius); + const float weight = indexw.second; + const float pot = mPotential[side](iZ, iR, iPhi); + const float potNew = weight * potentialFunc(radius); + if (std::abs(potNew) > std::abs(pot)) { + mPotential[side](iZ, iR, iPhi) = potNew; + } } } template -std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap) const +std::vector> SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap) const { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; - // to avoid double counting auto indices = getPotentialBoundaryGEMFrameAlongRIndices(side); - if (!bottom && outerFrame) { - // if OROC3 to OFC check outer GEM frame from OROC3! - const auto indicesOROC3 = getPotentialBoundaryGEMFrameAlongPhiIndices(GEMstack::OROC3gem, false, side, false); - indices.insert(indices.end(), indicesOROC3.begin(), indicesOROC3.end()); - std::sort(indices.begin(), indices.end()); - } else if (bottom && outerFrame) { - // if IROC to IFC check inner GEM frame from IROC - const auto indicesIROC = getPotentialBoundaryGEMFrameAlongPhiIndices(GEMstack::IROCgem, true, side, false); - indices.insert(indices.end(), indicesIROC.begin(), indicesIROC.end()); - std::sort(indices.begin(), indices.end()); - } - int region = 0; float offsStart = 0; float offsEnd = 0; @@ -415,10 +418,10 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndi nVerticesR = 1; } - std::vector potentialInd; - const int verticesPerSector = simOneSectorOnly ? mParamGrid.NPhiVertices : mParamGrid.NPhiVertices / SECTORSPERSIDE; - const auto nBinsPhi = (outerFrame || noGap) ? 0 : (simOneSectorOnly ? 0 : getPhiBinsGapFrame(side)); - for (int sector = 0; sector < (simOneSectorOnly ? 1 : SECTORSPERSIDE); ++sector) { + std::vector> potentialInd; // index, weight + const int verticesPerSector = mParamGrid.NPhiVertices / MGParameters::normalizeGridToNSector; + const auto nBinsPhi = (outerFrame || noGap) ? 0 : getPhiBinsGapFrame(side); + for (int sector = 0; sector < MGParameters::normalizeGridToNSector; ++sector) { const auto offsetPhi = sector * verticesPerSector + verticesPerSector / 2; for (size_t iPhiLocal = 0; iPhiLocal <= (verticesPerSector / 2 - nBinsPhi); ++iPhiLocal) { const auto iPhiLeft = offsetPhi + iPhiLocal; @@ -432,31 +435,62 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndi // end at gem frame if ((outerFrame && (stack == GEMstack::IROCgem))) { - nREnd = (radiusBottom - getRVertex(1, side)) / getGridSpacingR(side) + 2; // 2 safety margin + // TODO: remove this? + const float marginCM = 0; // 0.4; + const int nMargingBins = marginCM / getGridSpacingR(side) + 0.5; + nREnd = (radiusBottom - getRVertex(1, side) + 0.5) / getGridSpacingR(side) + nMargingBins; // 2 safety margin + radiusMax = 3 + getRVertex(getNearestRVertex(radiusBottom + getGridSpacingR(side) * nMargingBins, side), side); } - if (rStart == 0) { + rStart -= 1; + nREnd += 1; + if (rStart <= 0) { rStart = 1; } + if (nREnd >= mParamGrid.NRVertices) { + nREnd = mParamGrid.NRVertices - 1; + } + + float lxMin = radiusStart; + if ((outerFrame && (stack == GEMstack::IROCgem))) { + lxMin = 0; + } + const float lxMax = (nREnd == mParamGrid.NRVertices - 1) ? 9999 : radiusMax; for (size_t iR = rStart; iR < nREnd; ++iR) { const size_t iZ = mParamGrid.NZVertices - 1; + float weight = 1; if (iPhiLeft < getNPhiVertices()) { - if (noGap || !std::binary_search(indices.begin(), indices.end(), mPotential[side].getDataIndex(iZ, iR, iPhiLeft))) { - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft)); + if (noGap || !std::binary_search(indices.begin(), indices.end(), std::make_pair(mPotential[side].getDataIndex(iZ, iR, iPhiLeft), 0.0f), [](const auto& a, const auto& b) { return (a.first < b.first); })) { + + // check how much of the bin is in the lx range and assign weigth + const int nIterPoints = 1000; + int nPointsGood = 0; + for (int i = 0; i < nIterPoints; ++i) { + const float radius = getRVertex(iR, side) + getGridSpacingR(side) * gRandom->Uniform(-0.5, 0.5); + const float phi = getGridSpacingPhi(side) * gRandom->Uniform(-0.5, 0.5); + const DataT lx = radius * std::cos(phi + localphi); + if ((lx >= lxMin) && (lx <= lxMax)) { + ++nPointsGood; + } + } + weight = nPointsGood / double(nIterPoints); + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft), weight); } } - if (iPhiLocal && (noGap || !std::binary_search(indices.begin(), indices.end(), mPotential[side].getDataIndex(iZ, iR, iPhiRight)))) { - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight)); + if (iPhiLocal && (noGap || !std::binary_search(indices.begin(), indices.end(), std::make_pair(mPotential[side].getDataIndex(iZ, iR, iPhiRight), 0.0f), [](const auto& a, const auto& b) { return (a.first < b.first); }))) { + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight), weight); } } } } // remove duplicate entries - std::unordered_set set(potentialInd.begin(), potentialInd.end()); - potentialInd.assign(set.begin(), set.end()); std::sort(potentialInd.begin(), potentialInd.end()); + + // Remove duplicates + potentialInd.erase(std::unique(potentialInd.begin(), potentialInd.end()), potentialInd.end()); + return potentialInd; } @@ -1809,6 +1843,18 @@ DataT SpaceCharge::getDensityCyl(const DataT z, const DataT r, const Data return mInterpolatorDensity[side](z, r, phi); } +template +std::vector SpaceCharge::getDensityCyl(const std::vector& z, const std::vector& r, const std::vector& phi, const Side side) const +{ + const auto nPoints = z.size(); + std::vector density(nPoints); +#pragma omp parallel for num_threads(sNThreads) + for (size_t i = 0; i < nPoints; ++i) { + density[i] = getDensityCyl(z[i], r[i], phi[i], side); + } + return density; +} + template DataT SpaceCharge::getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const { @@ -1885,7 +1931,8 @@ void SpaceCharge::getCorrections(const DataT x, const DataT y, const Data } else { // convert cartesian to polar const DataT radius = getRadiusFromCartesian(x, y); - const DataT phi = getPhiFromCartesian(x, y); + DataT phi = getPhiFromCartesian(x, y); + o2::math_utils::detail::bringTo02PiGen(phi); DataT corrR{}; DataT corrRPhi{}; @@ -2421,7 +2468,7 @@ void SpaceCharge::makeElectronDriftPathGif(const char* inpFile, TH2F& hDu template void SpaceCharge::dumpToTree(const char* outFileName, const Side side, const int nZPoints, const int nRPoints, const int nPhiPoints, const bool randomize) const { - const DataT phiSpacing = GridProp::getGridSpacingPhi(nPhiPoints) / (MGParameters::normalizeGridToOneSector ? SECTORSPERSIDE : 1); + const DataT phiSpacing = GridProp::getGridSpacingPhi(nPhiPoints) * (MGParameters::normalizeGridToNSector / double(SECTORSPERSIDE)); const DataT rSpacing = GridProp::getGridSpacingR(nRPoints); const DataT zSpacing = side == Side::A ? GridProp::getGridSpacingZ(nZPoints) : -GridProp::getGridSpacingZ(nZPoints); @@ -2459,6 +2506,7 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co std::vector> lPosOut(nPhiPoints); std::vector> sectorOut(nPhiPoints); std::vector> globalIdxOut(nPhiPoints); + std::vector> isOnPadPlane(nPhiPoints); #pragma omp parallel for num_threads(sNThreads) for (int iPhi = 0; iPhi < nPhiPoints; ++iPhi) { @@ -2494,6 +2542,7 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co lPosOut[iPhi].reserve(nPoints); sectorOut[iPhi].reserve(nPoints); globalIdxOut[iPhi].reserve(nPoints); + isOnPadPlane[iPhi].reserve(nPoints); std::mt19937 rng(std::random_device{}()); DataT phiPos = iPhi * phiSpacing; @@ -2603,6 +2652,12 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co sectorOut[iPhi].emplace_back(sector); const size_t idx = (iZ + nZPoints * (iR + iPhi * nRPoints)); globalIdxOut[iPhi].emplace_back(idx); + + const float xDist = getXFromPolar(radiusDistorted, phiDistorted); + const float yDist = getYFromPolar(radiusDistorted, phiDistorted); + GlobalPosition3D posTmp(xDist, yDist, zPos); + const DigitPos digiPadPos = o2::tpc::Mapper::instance().findDigitPosFromGlobalPosition(posTmp); + isOnPadPlane[iPhi].emplace_back(digiPadPos.isValid()); } } } @@ -2645,6 +2700,7 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co dfStore = dfStore.DefineSlotEntry("bZ", [&bZOut = bZOut](unsigned int, ULong64_t entry) { return bZOut[entry]; }); dfStore = dfStore.DefineSlotEntry("bPhi", [&bPhiOut = bPhiOut](unsigned int, ULong64_t entry) { return bPhiOut[entry]; }); dfStore = dfStore.DefineSlotEntry("globalIndex", [&globalIdxOut = globalIdxOut](unsigned int, ULong64_t entry) { return globalIdxOut[entry]; }); + dfStore = dfStore.DefineSlotEntry("isOnPadPlane", [&isOnPadPlane = isOnPadPlane](unsigned int, ULong64_t entry) { return isOnPadPlane[entry]; }); dfStore.Snapshot("tree", outFileName); timer.Print("u"); } @@ -3356,20 +3412,20 @@ void SpaceCharge::readMetaData(std::string_view file) } template -void SpaceCharge::setSimOneSector() +void SpaceCharge::setSimNSector(const int nSectors) { LOGP(warning, "Use this feature only if you know what you are doing!"); - o2::tpc::MGParameters::normalizeGridToOneSector = true; - RegularGrid gridTmp[FNSIDES]{{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::A) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE, mParamGrid}, - {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::C) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE, mParamGrid}}; + o2::tpc::MGParameters::normalizeGridToNSector = nSectors; + RegularGrid gridTmp[FNSIDES]{{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::A) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE * nSectors, mParamGrid}, + {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::C) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE * nSectors, mParamGrid}}; mGrid3D[0] = gridTmp[0]; mGrid3D[1] = gridTmp[1]; } template -void SpaceCharge::unsetSimOneSector() +void SpaceCharge::unsetSimNSector() { - o2::tpc::MGParameters::normalizeGridToOneSector = false; + o2::tpc::MGParameters::normalizeGridToNSector = SECTORSPERSIDE; } template @@ -3424,6 +3480,20 @@ void SpaceCharge::addChargeDensity(const SpaceCharge& otherSC) mDensity[Side::C] += otherSC.mDensity[Side::C]; } +template +void SpaceCharge::addGlobalCorrections(const SpaceCharge& otherSC, const Side side) +{ + const bool sameGrid = (getNPhiVertices() == otherSC.getNPhiVertices()) && (getNRVertices() == otherSC.getNRVertices()) && (getNZVertices() == otherSC.getNZVertices()); + if (!sameGrid) { + LOGP(warning, "Space charge objects have different grid definition"); + return; + } + + mGlobalCorrdR[side] += otherSC.mGlobalCorrdR[side]; + mGlobalCorrdZ[side] += otherSC.mGlobalCorrdZ[side]; + mGlobalCorrdRPhi[side] += otherSC.mGlobalCorrdRPhi[side]; +} + template void SpaceCharge::fillChargeDensityFromHisto(const char* file, const char* nameA, const char* nameC) { @@ -3739,9 +3809,27 @@ void SpaceCharge::setIFCChargeUpFallingPot(const float deltaPot, const fl template void SpaceCharge::setGlobalCorrections(const std::function& gCorr, const Side side) { - initContainer(mGlobalCorrdR[side], true); - initContainer(mGlobalCorrdZ[side], true); - initContainer(mGlobalCorrdRPhi[side], true); + setGlobalDistCorr(Type::Corrections, gCorr, side); +} + +template +void SpaceCharge::setGlobalDistortions(const std::function& gDist, const Side side) +{ + setGlobalDistCorr(Type::Distortions, gDist, side); +} + +template +void SpaceCharge::setGlobalDistCorr(const Type type, const std::function& gFunc, const Side side) +{ + if (type == Type::Distortions) { + initContainer(mGlobalDistdR[side], true); + initContainer(mGlobalDistdZ[side], true); + initContainer(mGlobalDistdRPhi[side], true); + } else { + initContainer(mGlobalCorrdR[side], true); + initContainer(mGlobalCorrdZ[side], true); + initContainer(mGlobalCorrdRPhi[side], true); + } #pragma omp parallel for num_threads(sNThreads) for (unsigned int iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) { @@ -3761,7 +3849,7 @@ void SpaceCharge::setGlobalCorrections(const std::function::setGlobalCorrections(const std::function::setROCMisalignment(int stackType, int misalignmentType, } template -void SpaceCharge::fillROCMisalignment(const std::vector& indicesTop, const std::vector& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar) +void SpaceCharge::fillROCMisalignment(const std::vector>& indicesTop, const std::vector>& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar) { - for (const auto& index : indicesTop) { + for (const auto& indexw : indicesTop) { + const int index = indexw.first; const int iZ = DataContainer3D::getIndexZ(index, getNZVertices(), getNRVertices(), getNPhiVertices()); const int iRStart = DataContainer3D::getIndexR(index, getNZVertices(), getNRVertices(), getNPhiVertices()); const int iPhi = DataContainer3D::getIndexPhi(index, getNZVertices(), getNRVertices(), getNPhiVertices()); @@ -3853,7 +3948,8 @@ void SpaceCharge::fillROCMisalignment(const std::vector& indicesT for (size_t iR = iRStart; iR > 0; --iR) { const size_t currInd = (iZ + getNZVertices() * (iR + iPhi * getNRVertices())); - const bool foundVertexBottom = std::binary_search(indicesBottom.begin(), indicesBottom.end(), currInd); + const bool foundVertexBottom = std::binary_search(indicesBottom.begin(), indicesBottom.end(), std::make_pair(currInd, 0.0f), [](const auto& a, const auto& b) { return (a.first < b.first); }); + if (foundVertexBottom) { break; } @@ -3982,7 +4078,7 @@ void SpaceCharge::initAfterReadingFromFile() } template -float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, o2::utils::TreeStreamRedirector* pcstream) const +float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, float rStart, o2::utils::TreeStreamRedirector* pcstream) const { const float rmin = getRMin(o2::tpc::Side::A); std::vector dRphi; @@ -3990,7 +4086,7 @@ float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, dRphi.reserve(nPoints); r.reserve(nPoints); for (int i = 0; i < nPoints; ++i) { - float radius = rmin + i; + float radius = (rStart > 0) ? (rStart + i) : (rmin + i); float z = tgl * radius; DataT distZ = 0; DataT distR = 0; @@ -4030,6 +4126,7 @@ float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, << "r=" << r << "dRphi=" << dRphi << "tgl=" << tgl + << "phi=" << phi << "dca=" << dca << "rInterpol=" << rInterpol << "dRPhiInterpol=" << dRPhiInterpol @@ -4047,6 +4144,26 @@ void SpaceCharge::setPotential(int iz, int ir, int iphi, Side side, float mPotential[side](iz, ir, iphi) = val; } +template +void SpaceCharge::downSampleObject(const int nZNew, const int nRNew, const int nPhiNew) +{ + o2::tpc::SpaceCharge scNew(getBField(), nZNew, nRNew, nPhiNew); + for (int iside = 0; iside < 2; ++iside) { + const o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + const std::vector> dataRef{mLocalDistdR[iside], mLocalDistdZ[iside], mLocalDistdRPhi[iside], mLocalVecDistdR[iside], mLocalVecDistdZ[iside], mLocalVecDistdRPhi[iside], mLocalCorrdR[iside], mLocalCorrdZ[iside], mLocalCorrdRPhi[iside], mGlobalDistdR[iside], mGlobalDistdZ[iside], mGlobalDistdRPhi[iside], mGlobalCorrdR[iside], mGlobalCorrdZ[iside], mGlobalCorrdRPhi[iside], mDensity[iside], mPotential[iside], mElectricFieldEr[iside], mElectricFieldEz[iside], mElectricFieldEphi[iside]}; + const std::vector> dataNew{scNew.mLocalDistdR[iside], scNew.mLocalDistdZ[iside], scNew.mLocalDistdRPhi[iside], scNew.mLocalVecDistdR[iside], scNew.mLocalVecDistdZ[iside], scNew.mLocalVecDistdRPhi[iside], scNew.mLocalCorrdR[iside], scNew.mLocalCorrdZ[iside], scNew.mLocalCorrdRPhi[iside], scNew.mGlobalDistdR[iside], scNew.mGlobalDistdZ[iside], scNew.mGlobalDistdRPhi[iside], scNew.mGlobalCorrdR[iside], scNew.mGlobalCorrdZ[iside], scNew.mGlobalCorrdRPhi[iside], scNew.mDensity[iside], scNew.mPotential[iside], scNew.mElectricFieldEr[iside], scNew.mElectricFieldEz[iside], scNew.mElectricFieldEphi[iside]}; + for (int i = 0; i < dataRef.size(); ++i) { + const auto& objRef = dataRef[i].get(); + if (objRef.getNDataPoints()) { + auto& objNew = dataNew[i].get(); + scNew.initContainer(objNew, true); + objNew = objRef.convert(scNew.mGrid3D[iside], mGrid3D[iside], sNThreads); + } + } + } + *this = std::move(scNew); +} + using DataTD = double; template class o2::tpc::SpaceCharge; diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index fe7c9175968b5..6930f332bfbf1 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -70,17 +70,6 @@ o2_add_library(TPCWorkflowStudies O2::GlobalTrackingWorkflow ) -o2_add_executable(chunkeddigit-merger - COMPONENT_NAME tpc - TARGETVARNAME mergertargetName - SOURCES src/ChunkedDigitPublisher.cxx - PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) - -if(OpenMP_CXX_FOUND) - # Must be private, depending libraries might be compiled by compiler not understanding -fopenmp - target_compile_definitions(${mergertargetName} PRIVATE WITH_OPENMP) - target_link_libraries(${mergertargetName} PRIVATE OpenMP::OpenMP_CXX) -endif() o2_add_executable(reco-workflow @@ -211,7 +200,7 @@ o2_add_executable(idc-test-ft o2_add_executable(miptrack-filter COMPONENT_NAME tpc SOURCES src/tpc-miptrack-filter.cxx - PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow O2::GlobalTrackingWorkflow) o2_add_executable(track-and-cluster-filter COMPONENT_NAME tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h index 207bea0e7fa42..3ae33c7c2a5db 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h @@ -64,6 +64,10 @@ class CalibLaserTracksDevice : public o2::framework::Task return; } mTPCVDriftHelper.extractCCDBInputs(pc); + const auto timestamp = pc.services().get().creation; + + // if reference temperature / pressure of VDrift object is zero then it was not corrected + const float tp = (mTPCVDriftHelper.getVDriftObject().refTP == 0) ? mTPCVDriftHelper.getPTHelper().getTP(timestamp) : mTPCVDriftHelper.getVDriftObject().refTP; if (mTPCVDriftHelper.isUpdated()) { mTPCVDriftHelper.acknowledgeUpdate(); mCalib.setVDriftRef(mTPCVDriftHelper.getVDriftObject().getVDrift()); @@ -75,7 +79,7 @@ class CalibLaserTracksDevice : public o2::framework::Task auto data = pc.inputs().get>("input"); mCalib.setTFtimes(startTime, endTime); - mCalib.fill(data); + mCalib.fill(data, tp); if (!mOnlyPublishOnEOS && mCalib.hasEnoughData(mMinNumberTFs) && !mPublished) { sendOutput(pc.outputs()); diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibratorPadGainTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibratorPadGainTracksSpec.h index f9d5501196eb7..3ccef73a4a8fc 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibratorPadGainTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibratorPadGainTracksSpec.h @@ -22,7 +22,7 @@ #include "CommonUtils/NameConf.h" #include "Framework/Task.h" #include "Framework/ConfigParamRegistry.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsCalibration/Utils.h" diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h index d36391adfab51..767b68644d698 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace tpc class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none") : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -47,7 +47,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 = "none"); } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h index ac6fafec0a554..1b8483953a8ab 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h @@ -45,7 +45,7 @@ class VDriftHelper; class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool fromFile, bool selIR = false, std::shared_ptr pgg = std::shared_ptr()); + EntropyEncoderSpec(bool fromFile, bool selIR = false, std::shared_ptr pgg = std::shared_ptr(), const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -71,7 +71,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR = false, const std::string& ctfdictOpt = "none"); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h index 05024baad37b3..45406e6c01bbd 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h @@ -17,6 +17,8 @@ #define O2_TPC_MIPTRACKFILTERSPEC_H_ #include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +using GID = o2::dataformats::GlobalTrackID; using namespace o2::framework; @@ -24,7 +26,7 @@ namespace o2::tpc { /// create a processor spec -o2::framework::DataProcessorSpec getMIPTrackFilterSpec(); +o2::framework::DataProcessorSpec getMIPTrackFilterSpec(GID::mask_t srcTracks = GID::getSourcesMask("TPC")); } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h index f86afc310b04c..8e8a6a96eed63 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h @@ -35,15 +35,15 @@ struct CorrectionMapsLoaderGloOpts; namespace reco_workflow { /// define input and output types of the workflow -enum struct InputType { PassThrough, // No processing, just pass through available inputs to the writers, defined by the OutputType - Digitizer, // directly read digits from channel {TPC:DIGITS} - Digits, // read digits from file - ClustersHardware, // read hardware clusters in raw page format from file - Clusters, // read native clusters from file - CompClusters, // read compressed cluster container - CompClustersCTF, // compressed clusters from CTF, as flat format - CompClustersFlat, // compressed clusters in flat format, used as input for the entropy encoder - EncodedClusters, // read encoded clusters +enum struct InputType { PassThrough, // No processing, just pass through available inputs to the writers, defined by the OutputType + Digitizer, // directly read digits from channel {TPC:DIGITS} + Digits, // read digits from file + ClustersHardware, // read hardware clusters in raw page format from file + Clusters, // read native clusters from file + CompClustersRoot, // read compressed cluster in ROOT format + CompClustersFlat, // compressed clusters from flat format (e.g. from CTF) + CompClustersFlatForEncode, // compressed clusters in flat format, used as input for the entropy encoder, no gpu-reco + EncodedClusters, // read encoded clusters ZSRaw, }; @@ -59,7 +59,8 @@ enum struct OutputType { Digits, ClustersHardware, Clusters, Tracks, - CompClusters, + CompClustersRoot, + CompClustersFlat, EncodedClusters, DisableWriter, SendClustersPerSector, @@ -84,6 +85,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, int caClusterer = 0, // int zsOnTheFly = 0, bool askDISTSTF = true, + const std::string& ctfdictOpt = "none", bool selIR = false, bool filteredInp = false, int deadMapSources = -1, diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h index 7afc973d7a3ab..516ea128acfe7 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h @@ -25,7 +25,7 @@ #include "DataFormatsParameters/GRPObject.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "Framework/CCDBParamSpec.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -54,6 +54,7 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task } mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } void init(o2::framework::InitContext& ic) final @@ -139,7 +140,7 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task if (matcher == ConcreteDataMatcher(gDataOriginTPC, "RESIDUALGAINMAP", 0)) { if (!mUsingDefaultGainMapForFirstIter) { LOGP(info, "Updating reference gain map from previous iteration from CCDB"); - const auto* gainMapResidual = static_cast>*>(obj); + const auto* gainMapResidual = static_cast>*>(obj); mPadGainTracks.setRefGainMap(gainMapResidual->at("GainMap")); } else { // just skip for the first time asking for an object -> not gain map will be used as reference diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadRawSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadRawSpec.h index 19cbeb05f7007..7579e334ff267 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadRawSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadRawSpec.h @@ -34,7 +34,7 @@ #include "DetectorsBase/TFIDInfoHelper.h" #include "DataFormatsTPC/TPCSectorHeader.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCCalibration/CalibPedestal.h" #include "TPCCalibration/CalibPulser.h" #include "TPCReconstruction/RawReaderCRU.h" diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPIDCSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPIDCSpec.h index ec3e158590661..e4b85ad7c04d9 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPIDCSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPIDCSpec.h @@ -31,7 +31,7 @@ #include "TPCCalibration/IDCFactorization.h" #include "Framework/CCDBParamSpec.h" #include "TPCWorkflow/ProcessingHelpers.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" using namespace o2::framework; using o2::header::gDataOriginTPC; diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeIDCSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeIDCSpec.h index 667386e6481ca..c8384cf9c9264 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeIDCSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeIDCSpec.h @@ -35,7 +35,7 @@ #include "TPCBase/CRU.h" #include "CommonUtils/NameConf.h" #include "TPCWorkflow/ProcessingHelpers.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "DetectorsCalibration/Utils.h" #include "TPCCalibration/IDCCCDBHelper.h" diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeSACSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeSACSpec.h index f191f5f44761b..1757f3e223e86 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeSACSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFactorizeSACSpec.h @@ -29,7 +29,7 @@ #include "CCDB/CcdbApi.h" #include "TPCWorkflow/TPCDistributeSACSpec.h" #include "TPCWorkflow/ProcessingHelpers.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "DetectorsCalibration/Utils.h" #include "Framework/InputRecordWalker.h" diff --git a/Detectors/TPC/workflow/readers/CMakeLists.txt b/Detectors/TPC/workflow/readers/CMakeLists.txt index 80e967c287404..28d101caf188c 100644 --- a/Detectors/TPC/workflow/readers/CMakeLists.txt +++ b/Detectors/TPC/workflow/readers/CMakeLists.txt @@ -21,9 +21,3 @@ o2_add_library(TPCReaderWorkflow O2::DPLUtils O2::TPCBase ) - -if(OpenMP_CXX_FOUND) - # Must be private, depending libraries might be compiled by compiler not understanding -fopenmp - target_compile_definitions(${mergertargetName} PRIVATE WITH_OPENMP) - target_link_libraries(${mergertargetName} PRIVATE OpenMP::OpenMP_CXX) -endif() diff --git a/Detectors/TPC/workflow/src/CalDetMergerPublisherSpec.cxx b/Detectors/TPC/workflow/src/CalDetMergerPublisherSpec.cxx index a504ffa606b84..bb3c927e3df4d 100644 --- a/Detectors/TPC/workflow/src/CalDetMergerPublisherSpec.cxx +++ b/Detectors/TPC/workflow/src/CalDetMergerPublisherSpec.cxx @@ -36,7 +36,7 @@ #include "DetectorsCalibration/Utils.h" #include "CCDB/CcdbApi.h" #include "CCDB/CcdbObjectInfo.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/CalDet.h" #include "TPCBase/CRUCalibHelpers.h" #include "TPCWorkflow/CalibRawPartInfo.h" diff --git a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx index 5cf412f227d78..7c2e2db8188e8 100644 --- a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx @@ -26,10 +26,10 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" -#include "GPUO2InterfaceConfigurableParam.h" +#include "GPUO2ConfigurableParam.h" #include "TPCCalibration/CalibdEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" -#include "TPCBase/CDBTypes.h" +#include "TPCBaseRecSim/CDBTypes.h" #include "TPCBase/Utils.h" #include "DetectorsBase/GRPGeomHelper.h" diff --git a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx index ce45356aa28c8..87e339f0643f4 100644 --- a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx @@ -29,11 +29,11 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" -#include "GPUO2InterfaceConfigurableParam.h" +#include "GPUO2ConfigurableParam.h" #include "TPCCalibration/CalibratordEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "DetectorsBase/GRPGeomHelper.h" -#include "TPCBase/CDBTypes.h" +#include "TPCBaseRecSim/CDBTypes.h" #include "TPCBase/Utils.h" using namespace o2::framework; @@ -235,7 +235,7 @@ DataProcessorSpec getCalibratordEdxSpec(const o2::base::Propagator::MatCorrType Options{ {"tf-per-slot", VariantType::UInt32, 6000u, {"number of TFs per calibration time slot, is overwritten by seconds-per-slot if > 0"}}, {"seconds-per-slot", VariantType::Int, 180, {"seconds per calibration time slot, overwrites tf-per-slot if > 0"}}, - {"max-delay", VariantType::UInt32, 10u, {"number of slots in past to consider"}}, + {"max-delay", VariantType::UInt32, 1u, {"number of slots in past to consider"}}, {"min-entries", VariantType::Int, 10000, {"minimum entries per stack to fit a single time slot"}}, {"calib-interval-extension", VariantType::UInt32, 3600u, {"seconds by which to extend the calibration interval beyond the end of the time slot"}}, diff --git a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx index 4ff3573918722..dd73d582553e6 100644 --- a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx @@ -26,7 +26,6 @@ namespace o2 { namespace tpc { - void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { if (mCTFCoder.finaliseCCDB(matcher, obj)) { @@ -66,11 +65,14 @@ 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 inputs; inputs.emplace_back("ctf_TPC", "TPC", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TPC", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TPC", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ @@ -79,10 +81,8 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) Outputs{OutputSpec{{"output"}, "TPC", "COMPCLUSTERSFLAT", 0, Lifetime::Timeframe}, OutputSpec{{"trigger"}, "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}, OutputSpec{{"ctfrep"}, "TPC", "CTFDECREP", 0, Lifetime::Timeframe}}, - 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 tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index 2efa7077be125..73bdfa1905f3b 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -38,10 +38,9 @@ namespace o2 { namespace tpc { - EntropyEncoderSpec::~EntropyEncoderSpec() = default; -EntropyEncoderSpec::EntropyEncoderSpec(bool fromFile, bool selIR, std::shared_ptr pgg) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mFromFile(fromFile), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool fromFile, bool selIR, std::shared_ptr pgg, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mFromFile(fromFile), mSelIR(selIR) { if (mSelIR) { mGRPRequest = pgg; @@ -305,13 +304,16 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR, const std::string& ctfdictOpt) { std::vector inputs; header::DataDescription inputType = inputFromFile ? header::DataDescription("COMPCLUSTERS") : header::DataDescription("COMPCLUSTERSFLAT"); inputs.emplace_back("input", "TPC", inputType, 0, Lifetime::Timeframe); inputs.emplace_back("trigger", "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + } std::shared_ptr ggreq; if (selIR) { @@ -324,9 +326,8 @@ DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) inputs, Outputs{{"TPC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TPC", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(inputFromFile, selIR, ggreq)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"no-ctf-columns-combining", VariantType::Bool, false, {"Do not combine correlated columns in CTF"}}, + AlgorithmSpec{adaptFromTask(inputFromFile, selIR, ggreq, ctfdictOpt)}, + Options{{"no-ctf-columns-combining", VariantType::Bool, false, {"Do not combine correlated columns in CTF"}}, {"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"}}, {"irframe-clusters-maxeta", VariantType::Float, 1.5f, {"Max eta for non-assigned clusters"}}, @@ -335,6 +336,5 @@ DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) {"nThreads-tpc-encoder", VariantType::UInt32, 1u, {"number of threads to use for decoding"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx b/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx index 27dbcf5d85bbf..da8de5f262cdf 100644 --- a/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx +++ b/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx @@ -72,6 +72,7 @@ class IDCToVectorDevice : public o2::framework::Task mWriteDebugOnError = ic.options().get("write-debug-on-error"); mWriteRawDataOnError = ic.options().get("write-raw-data-on-error"); mRawDataType = ic.options().get("raw-data-type"); + o2::framework::RawParser<>::setCheckIncompleteHBF(ic.options().get("check-incomplete-hbf")); mDebugStreamFileName = ic.options().get("debug-file-name").data(); mRawOutputFileName = ic.options().get("raw-file-name").data(); @@ -405,7 +406,7 @@ class IDCToVectorDevice : public o2::framework::Task for (const auto& inf : infVec) { if (!inf.hasBothEPs()) { - LOGP(error, "IDC CRU {:3}: data missing at ({:8}, {:4}) for one or both end points {:02b} in TF {}", cru, inf.heartbeatOrbit, inf.heartbeatBC, inf.epSeen, tfCounter); + LOGP(warning, "IDC CRU {:3}: data missing at ({:8}, {:4}) for one or both end points {:02b} in TF {}", cru, inf.heartbeatOrbit, inf.heartbeatBC, inf.epSeen, tfCounter); hasErrors = true; } } @@ -422,7 +423,7 @@ class IDCToVectorDevice : public o2::framework::Task } if (!std::equal(infVecComp->begin(), infVecComp->end(), infVec.begin())) { - LOGP(error, "IDC CRU {:3}: mismatch in orbit numbers", cru); + LOGP(warning, "IDC CRU {:3}: mismatch in orbit numbers", cru); hasErrors = true; } } @@ -606,9 +607,10 @@ o2::framework::DataProcessorSpec getIDCToVectorSpec(const std::string inputSpec, {"write-raw-data-on-error", VariantType::Bool, false, {"dump raw data in case errors occurred"}}, {"raw-file-name", VariantType::String, "/tmp/idc_debug.{run}.{raw_type}", {"name of the raw output file"}}, {"raw-data-type", VariantType::Int, 0, {"Which raw data to dump: 0-full TPC with DH, 1-full TPC with DH skip empty, 2-full TPC no DH, 3-full TPC no DH skip empty, 4-IDC raw only"}}, + {"check-incomplete-hbf", VariantType::Bool, false, {"false: don't chck; true: check and report"}}, {"pedestal-url", VariantType::String, "ccdb-default", {"ccdb-default: load from NameConf::getCCDBServer() OR ccdb url (must contain 'ccdb' OR pedestal file name"}}, {"swap-links", VariantType::Bool, false, {"swap links to circumvent bug in FW"}}, } // end Options - }; // end DataProcessorSpec + }; // end DataProcessorSpec } } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx b/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx index 33b9039298264..eff1a694a4727 100644 --- a/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx +++ b/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx @@ -16,7 +16,6 @@ #include "TPCWorkflow/MIPTrackFilterSpec.h" #include -#include #include #include #include @@ -24,7 +23,7 @@ // o2 includes #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/TrackCuts.h" -#include "DetectorsCalibration/Utils.h" +#include "Framework/CCDBParamSpec.h" #include "Framework/Logger.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/Task.h" @@ -33,8 +32,14 @@ #include "Framework/ConfigParamRegistry.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "Headers/DataHeader.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" using namespace o2::framework; +using DataRequest = o2::globaltracking::DataRequest; +using GID = o2::dataformats::GlobalTrackID; namespace o2::tpc { @@ -42,7 +47,8 @@ namespace o2::tpc class MIPTrackFilterDevice : public Task { public: - MIPTrackFilterDevice(std::shared_ptr gr) : mGRPGeomRequest(gr) {} + MIPTrackFilterDevice(std::shared_ptr gr, std::shared_ptr dr, GID::mask_t trackSourcesMask) + : mGRPGeomRequest(gr), mDataRequest(dr), mTrackSourcesMask(trackSourcesMask) {} void init(framework::InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -53,16 +59,20 @@ class MIPTrackFilterDevice : public Task void sendOutput(DataAllocator& output); std::shared_ptr mGRPGeomRequest; - TrackCuts mCuts{}; ///< Tracks cuts object - std::vector mMIPTracks; ///< Filtered MIP tracks - unsigned int mProcessEveryNthTF{1}; ///< process every Nth TF only - int mMaxTracksPerTF{-1}; ///< max number of MIP tracks processed per TF - uint32_t mTFCounter{0}; ///< counter to keep track of the TFs - int mProcessNFirstTFs{0}; ///< number of first TFs which are not sampled - float mDCACut{-1}; ///< DCA cut - bool mSendDummy{false}; ///< send empty data in case TF is skipped - - bool acceptDCA(const TrackTPC& track); + std::shared_ptr mDataRequest; + GID::mask_t mTrackSourcesMask; + TrackCuts mCuts{}; ///< Tracks cuts object + std::vector mMIPTracks; ///< Filtered MIP tracks + o2::dataformats::MeanVertexObject mVtx; ///< Mean vertex object + unsigned int mProcessEveryNthTF{1}; ///< process every Nth TF only + int mMaxTracksPerTF{-1}; ///< max number of MIP tracks processed per TF + uint32_t mTFCounter{0}; ///< counter to keep track of the TFs + int mProcessNFirstTFs{0}; ///< number of first TFs which are not sampled + float mDCACut{-1}; ///< DCA cut + float mDCAZCut{-1}; ///< DCA z cut + bool mSendDummy{false}; ///< send empty data in case TF is skipped + + bool acceptDCA(o2::track::TrackPar propTrack, o2::math_utils::Point3D refPoint, bool useDCAz = false); }; void MIPTrackFilterDevice::init(framework::InitContext& ic) @@ -100,6 +110,7 @@ void MIPTrackFilterDevice::init(framework::InitContext& ic) mCuts.setCutLooper(cutLoopers); mDCACut = ic.options().get("dca-cut"); + mDCAZCut = ic.options().get("dca-z-cut"); o2::base::GRPGeomHelper::instance().setRequest(mGRPGeomRequest); } @@ -107,6 +118,8 @@ void MIPTrackFilterDevice::init(framework::InitContext& ic) void MIPTrackFilterDevice::run(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + pc.inputs().get("meanvtx"); + const auto currentTF = processing_helpers::getCurrentTF(pc); if ((mTFCounter++ % mProcessEveryNthTF) && (currentTF >= mProcessNFirstTFs)) { LOGP(info, "Skipping TF {}", currentTF); @@ -117,19 +130,60 @@ void MIPTrackFilterDevice::run(ProcessingContext& pc) return; } - const auto tracks = pc.inputs().get>("tracks"); - const auto nTracks = tracks.size(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest); + const auto tracksTPC = recoData.getTPCTracks(); + const auto nTracks = tracksTPC.size(); + + // indices to good tracks + std::vector indices; + indices.reserve(nTracks); + + const auto useGlobalTracks = mTrackSourcesMask[GID::ITSTPC]; + o2::math_utils::Point3D vertex = mVtx.getXYZ(); + + if (useGlobalTracks) { + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + std::vector selSrc{GID::ITSTPC, GID::ITSTPCTRD, GID::ITSTPCTRDTOF}; // for Instance + // LOGP(info, "Number of vertex tracks: {}", vtxRefs.size()); + const auto nv = (vtxRefs.size() > 0) ? vtxRefs.size() - 1 : 0; // note: the last entry groups the tracks which were not related to any vertex, to skip them, use vtxRefs.size()-1 + + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + // LOGP(info, "Processing vertex {} with {} tracks", iv, vtref.getEntries()); + vertex = recoData.getPrimaryVertex(iv).getXYZ(); + // LOGP(info, "Vertex position: x={} y={} z={}", vertex.x(), vertex.y(), vertex.z()); + + for (auto src : selSrc) { + int idMin = vtxRefs[iv].getFirstEntryOfSource(src), idMax = idMin + vtxRefs[iv].getEntriesOfSource(src); + // LOGP(info, "Source {}: idMin={} idMax={}", GID::getSourceName(src), idMin, idMax); + + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + const auto& track = recoData.getTrackParam(vid); // this is a COPY of the track param which we will modify during DCA calculation + auto gidTPC = recoData.getTPCContributorGID(vid); + if (gidTPC.isSourceSet()) { + const auto idxTPC = gidTPC.getIndex(); + if (mCuts.goodTrack(tracksTPC[idxTPC]) && acceptDCA(tracksTPC[idxTPC], vertex, true)) { + indices.emplace_back(idxTPC); + } + } + } + } + } - if ((mMaxTracksPerTF != -1) && (nTracks > mMaxTracksPerTF)) { - // indices to good tracks - std::vector indices; - indices.reserve(nTracks); + } else { for (size_t i = 0; i < nTracks; ++i) { - if (mCuts.goodTrack(tracks[i]) && acceptDCA(tracks[i])) { + if (mCuts.goodTrack(tracksTPC[i]) && acceptDCA(tracksTPC[i], vertex)) { indices.emplace_back(i); } } + } + + size_t nTracksSel = indices.size(); + if ((mMaxTracksPerTF != -1) && (nTracksSel > mMaxTracksPerTF)) { // in case no good tracks have been found if (indices.empty()) { mMIPTracks.clear(); @@ -144,15 +198,14 @@ void MIPTrackFilterDevice::run(ProcessingContext& pc) std::shuffle(indices.begin(), indices.end(), rng); // copy good tracks - const int loopEnd = (mMaxTracksPerTF > indices.size()) ? indices.size() : mMaxTracksPerTF; - for (int i = 0; i < loopEnd; ++i) { - mMIPTracks.emplace_back(tracks[indices[i]]); - } - } else { - std::copy_if(tracks.begin(), tracks.end(), std::back_inserter(mMIPTracks), [this](const auto& track) { return mCuts.goodTrack(track) && acceptDCA(track); }); + nTracksSel = (mMaxTracksPerTF > indices.size()) ? indices.size() : mMaxTracksPerTF; + } + + for (int i = 0; i < nTracksSel; ++i) { + mMIPTracks.emplace_back(tracksTPC[indices[i]]); } - LOGP(info, "Filtered {} MIP tracks out of {} total tpc tracks", mMIPTracks.size(), tracks.size()); + LOGP(info, "Filtered {} / {} MIP tracks out of {} total tpc tracks, using {}", mMIPTracks.size(), indices.size(), tracksTPC.size(), useGlobalTracks ? "global tracks" : "TPC only tracks"); sendOutput(pc.outputs()); mMIPTracks.clear(); } @@ -162,6 +215,11 @@ void MIPTrackFilterDevice::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Setting new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } } void MIPTrackFilterDevice::sendOutput(DataAllocator& output) { output.snapshot(Output{header::gDataOriginTPC, "MIPS", 0}, mMIPTracks); } @@ -171,7 +229,7 @@ void MIPTrackFilterDevice::endOfStream(EndOfStreamContext& eos) LOG(info) << "Finalizig MIP Tracks filter"; } -bool MIPTrackFilterDevice::acceptDCA(const TrackTPC& track) +bool MIPTrackFilterDevice::acceptDCA(o2::track::TrackPar propTrack, o2::math_utils::Point3D refPoint, bool useDCAz) { if (mDCACut < 0) { return true; @@ -179,21 +237,21 @@ bool MIPTrackFilterDevice::acceptDCA(const TrackTPC& track) auto propagator = o2::base::Propagator::Instance(); std::array dca; - const o2::math_utils::Point3D refPoint{0, 0, 0}; - o2::track::TrackPar propTrack(track); const auto ok = propagator->propagateToDCABxByBz(refPoint, propTrack, 2., o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dca); const auto dcar = std::abs(dca[0]); - return ok && (dcar < mDCACut); + return ok && (dcar < mDCACut) && (!useDCAz || (std::abs(dca[1]) < mDCAZCut)); } -DataProcessorSpec getMIPTrackFilterSpec() +DataProcessorSpec getMIPTrackFilterSpec(GID::mask_t srcTracks) { std::vector outputs; outputs.emplace_back(header::gDataOriginTPC, "MIPS", 0, Lifetime::Sporadic); - std::vector inputs; - inputs.emplace_back("tracks", "TPC", "TRACKS"); + const auto useMC = false; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestPrimaryVertices(useMC); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true @@ -201,14 +259,16 @@ DataProcessorSpec getMIPTrackFilterSpec() true, // GRPMagField true, // askMatLUT o2::base::GRPGeomRequest::Aligned, // geometry - inputs, + dataRequest->inputs, true); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, o2::framework::ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + return DataProcessorSpec{ "tpc-miptrack-filter", - inputs, + dataRequest->inputs, outputs, - adaptFromTask(ggRequest), + adaptFromTask(ggRequest, dataRequest, srcTracks), Options{ {"min-momentum", VariantType::Double, 0.35, {"minimum momentum cut"}}, {"max-momentum", VariantType::Double, 0.55, {"maximum momentum cut"}}, @@ -220,7 +280,8 @@ DataProcessorSpec getMIPTrackFilterSpec() {"process-first-n-TFs", VariantType::Int, 1, {"Number of first TFs which are not sampled"}}, {"send-dummy-data", VariantType::Bool, false, {"Send empty data in case TF is skipped"}}, {"dont-cut-loopers", VariantType::Bool, false, {"Do not cut loopers by comparing zout-zin"}}, - {"dca-cut", VariantType::Float, 3.f, {"DCA cut in cm, < 0 to disable"}}, + {"dca-cut", VariantType::Float, 3.f, {"DCA cut in xy (cm), < 0 to disable cut in xy and z"}}, + {"dca-z-cut", VariantType::Float, 5.f, {"DCA cut in z (cm)"}}, }}; } diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 0edd23de7c57d..3054dd5d61519 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -80,16 +80,17 @@ const std::unordered_map InputMap{ {"clustershardware", InputType::ClustersHardware}, {"clusters", InputType::Clusters}, {"zsraw", InputType::ZSRaw}, - {"compressed-clusters", InputType::CompClusters}, - {"compressed-clusters-ctf", InputType::CompClustersCTF}, - {"compressed-clusters-flat", InputType::CompClustersFlat}}; + {"compressed-clusters-root", InputType::CompClustersRoot}, + {"compressed-clusters-flat", InputType::CompClustersFlat}, + {"compressed-clusters-flat-for-encode", InputType::CompClustersFlatForEncode}}; const std::unordered_map OutputMap{ {"digits", OutputType::Digits}, {"clustershardware", OutputType::ClustersHardware}, {"clusters", OutputType::Clusters}, {"tracks", OutputType::Tracks}, - {"compressed-clusters", OutputType::CompClusters}, + {"compressed-clusters-root", OutputType::CompClustersRoot}, + {"compressed-clusters-flat", OutputType::CompClustersFlat}, {"encoded-clusters", OutputType::EncodedClusters}, {"disable-writer", OutputType::DisableWriter}, {"send-clusters-per-sector", OutputType::SendClustersPerSector}, @@ -100,7 +101,7 @@ const std::unordered_map OutputMap{ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vector const& tpcSectors, unsigned long tpcSectorMask, std::vector const& laneConfiguration, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool propagateMC, unsigned nLanes, std::string const& cfgInput, std::string const& cfgOutput, bool disableRootInput, - int caClusterer, int zsOnTheFly, bool askDISTSTF, bool selIR, bool filteredInp, int deadMapSources, bool useMCTimeGain) + int caClusterer, int zsOnTheFly, bool askDISTSTF, const std::string& ctfdictOpt, bool selIR, bool filteredInp, int deadMapSources, bool useMCTimeGain) { InputType inputType; try { @@ -118,18 +119,23 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto return std::find(outputTypes.begin(), outputTypes.end(), type) != outputTypes.end(); }; - if (filteredInp && !(inputType == InputType::PassThrough && isEnabled(OutputType::Tracks) && isEnabled(OutputType::Clusters) && isEnabled(OutputType::SendClustersPerSector))) { - throw std::invalid_argument("filtered-input option must be provided only with pass-through input and clusters,tracks,send-clusters-per-sector output"); + if (filteredInp && !(inputType == InputType::PassThrough)) { + throw std::invalid_argument("filtered-input option must be provided only with pass-through input"); } - bool decompressTPC = inputType == InputType::CompClustersCTF || inputType == InputType::CompClusters; + bool decompressTPC = inputType == InputType::CompClustersFlat || inputType == InputType::CompClustersRoot; // Disable not applicable settings depending on TPC input, no need to disable manually if (decompressTPC && (isEnabled(OutputType::Clusters) || isEnabled(OutputType::Tracks))) { caClusterer = false; zsOnTheFly = false; propagateMC = false; } - if (inputType == InputType::ZSRaw || inputType == InputType::CompClustersFlat) { + if (inputType == InputType::CompClustersFlatForEncode || inputType == InputType::CompClustersRoot || inputType == InputType::CompClustersFlat) { + caClusterer = false; + zsOnTheFly = false; + propagateMC = false; + } + if (inputType == InputType::ZSRaw) { caClusterer = true; zsOnTheFly = false; propagateMC = false; @@ -159,6 +165,8 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto WorkflowSpec specs; + bool produceTracks = isEnabled(OutputType::Tracks); + // We provide a special publishing method for labels which have been stored in a split format and need // to be transformed into a contiguous shareable container before publishing. For other branches/types this returns // false and the generic RootTreeWriter publishing proceeds @@ -196,7 +204,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (sclOpts.needTPCScalersWorkflow()) { // for standalone tpc-reco workflow specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == 2, sclOpts.enableMShapeCorrection)); } - if (sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader + if (produceTracks && sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } } else if (inputType == InputType::ClustersHardware) { @@ -223,7 +231,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } - } else if (inputType == InputType::CompClusters) { + } else if (inputType == InputType::CompClustersRoot) { // TODO: need to check if we want to store the MC labels alongside with compressed clusters // for the moment reading of labels is disabled (last parameter is false) // TODO: make a different publisher spec for only one output spec, for now using the @@ -246,9 +254,9 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // output matrix // Note: the ClusterHardware format is probably a deprecated legacy format and also the // ClusterDecoderRawSpec - bool produceCompClusters = isEnabled(OutputType::CompClusters); - bool produceTracks = isEnabled(OutputType::Tracks); - bool runGPUReco = (produceTracks || produceCompClusters || (isEnabled(OutputType::Clusters) && caClusterer) || inputType == InputType::CompClustersCTF) && inputType != InputType::CompClustersFlat; + bool produceCompClustersRoot = isEnabled(OutputType::CompClustersRoot); + bool produceCompClustersFlat = isEnabled(OutputType::CompClustersFlat); + bool runGPUReco = (produceTracks || produceCompClustersRoot || produceCompClustersFlat || (isEnabled(OutputType::Clusters) && caClusterer) || inputType == InputType::CompClustersFlat) && inputType != InputType::CompClustersFlatForEncode; bool runHWDecoder = !caClusterer && (runGPUReco || isEnabled(OutputType::Clusters)); bool runClusterer = !caClusterer && (runHWDecoder || isEnabled(OutputType::ClustersHardware)); bool zsDecoder = inputType == InputType::ZSRaw; @@ -433,7 +441,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto (caClusterer || decompressTPC || inputType == InputType::PassThrough) && !isEnabled(OutputType::SendClustersPerSector))); } - if ((isEnabled(OutputType::TPCTriggers) || caClusterer) && !isEnabled(OutputType::DisableWriter)) { + if ((isEnabled(OutputType::TPCTriggers) || (caClusterer && runGPUReco)) && !isEnabled(OutputType::DisableWriter)) { specs.push_back(o2::tpc::getTPCTriggerWriterSpec()); } @@ -455,16 +463,17 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto cfg.runTPCTracking = true; cfg.lumiScaleType = sclOpts.lumiType; cfg.lumiScaleMode = sclOpts.lumiMode; + cfg.checkCTPIDCconsistency = sclOpts.checkCTPIDCconsistency; cfg.enableMShape = sclOpts.enableMShapeCorrection; cfg.enableCTPLumi = sclOpts.requestCTPLumi; cfg.decompressTPC = decompressTPC; - cfg.decompressTPCFromROOT = decompressTPC && inputType == InputType::CompClusters; + cfg.decompressTPCFromROOT = decompressTPC && inputType == InputType::CompClustersRoot; cfg.caClusterer = caClusterer; cfg.zsDecoder = zsDecoder; cfg.zsOnTheFly = zsOnTheFly; cfg.outputTracks = produceTracks; - cfg.outputCompClusters = produceCompClusters; - cfg.outputCompClustersFlat = runClusterEncoder; + cfg.outputCompClustersRoot = produceCompClustersRoot; + cfg.outputCompClustersFlat = produceCompClustersFlat || runClusterEncoder; cfg.outputCAClusters = isEnabled(OutputType::Clusters) && (caClusterer || decompressTPC); cfg.outputQA = isEnabled(OutputType::QA); cfg.outputSharedClusterMap = (isEnabled(OutputType::Clusters) || inputType == InputType::Clusters) && isEnabled(OutputType::Tracks) && !isEnabled(OutputType::NoSharedClusterMap); @@ -498,7 +507,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // // selected by output type 'encoded-clusters' if (runClusterEncoder) { - specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlat, selIR)); + specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlatForEncode, selIR, ctfdictOpt)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -544,8 +553,8 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // // a writer process for compressed clusters container // - // selected by output type 'compressed-clusters' - if (produceCompClusters && !isEnabled(OutputType::DisableWriter)) { + // selected by output type 'compressed-clusters-root' + if (produceCompClustersRoot && !isEnabled(OutputType::DisableWriter)) { // defining the track writer process using the generic RootTreeWriter and generator tool // // defaults diff --git a/Detectors/TPC/workflow/src/SACProcessorSpec.cxx b/Detectors/TPC/workflow/src/SACProcessorSpec.cxx index e69533a0bb6d3..1d09b9f0a4fbe 100644 --- a/Detectors/TPC/workflow/src/SACProcessorSpec.cxx +++ b/Detectors/TPC/workflow/src/SACProcessorSpec.cxx @@ -25,7 +25,7 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/CCDBParamSpec.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "DataFormatsTPC/RawDataTypes.h" #include "TPCBase/RDHUtils.h" diff --git a/Detectors/TPC/workflow/src/TPCMergeIntegrateClusterSpec.cxx b/Detectors/TPC/workflow/src/TPCMergeIntegrateClusterSpec.cxx index 01538aab5ad90..2fdf0d001f475 100644 --- a/Detectors/TPC/workflow/src/TPCMergeIntegrateClusterSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCMergeIntegrateClusterSpec.cxx @@ -25,7 +25,7 @@ #include "DetectorsCommonDataFormats/FileMetaData.h" #include "Framework/DataTakingContext.h" #include "TPCCalibration/IDCFactorization.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/CalDet.h" #include diff --git a/Detectors/TPC/workflow/src/TPCRefitter.cxx b/Detectors/TPC/workflow/src/TPCRefitter.cxx index b2e41c8e808da..51ff2516524a9 100644 --- a/Detectors/TPC/workflow/src/TPCRefitter.cxx +++ b/Detectors/TPC/workflow/src/TPCRefitter.cxx @@ -68,6 +68,7 @@ class TPCRefitterSpec final : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TPCRefitterSpec() final = default; void init(InitContext& ic) final; diff --git a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx index 6065079c05e96..f185b5e08c7e7 100644 --- a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx @@ -19,7 +19,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/CCDBParamSpec.h" #include "Framework/ConfigParamRegistry.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TPCCalibration/TPCScaler.h" #include "TPCCalibration/TPCMShapeCorrection.h" diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx index a9f1e7d71da8e..ee3acc808ccb7 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -19,7 +19,6 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ControlService.h" -#include "DataFormatsTPC/WorkflowHelper.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "TPCBase/Mapper.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -31,7 +30,6 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "MathUtils/Tsallis.h" #include "ReconstructionDataFormats/TrackTPCITS.h" -#include "CommonDataFormat/AbstractRefAccessor.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" #include "ReconstructionDataFormats/VtxTrackRef.h" @@ -46,6 +44,7 @@ #include "ReconstructionDataFormats/MatchInfoTOF.h" #include "DataFormatsTOF/Cluster.h" #include "DataFormatsFT0/RecPoints.h" +#include "TPCCalibration/PressureTemperatureHelper.h" using namespace o2::globaltracking; using GTrackID = o2::dataformats::GlobalTrackID; @@ -127,16 +126,20 @@ class TPCTimeSeries : public Task { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); + mPTHelper.extractCCDBInputs(pc); if (mTPCVDriftHelper.isUpdated()) { mTPCVDriftHelper.acknowledgeUpdate(); mVDrift = mTPCVDriftHelper.getVDriftObject().getVDrift(); LOGP(info, "Updated reference drift velocity to: {}", mVDrift); } + mBufferDCA.mVDrift = mVDrift; const int nBins = getNBins(); mTimeMS = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS() + processing_helpers::getFirstTForbit(pc) * o2::constants::lhc::LHCOrbitMUS / 1000; mRun = processing_helpers::getRunNumber(pc); + mBufferDCA.mTemperature = mPTHelper.getMeanTemperature(mTimeMS); + mBufferDCA.mPressure = mPTHelper.getPressure(mTimeMS); // init only once if (mAvgADCAr.size() != nBins) { @@ -207,14 +210,14 @@ class TPCTimeSeries : public Task indicesITSTPC[tracksITSTPC[i].getRefTPC().getIndex()] = {i, idxVtx}; } - std::vector> idxTPCTrackToTOFCluster; // store for each tpc track index the index to the TOF cluster + std::vector> idxTPCTrackToTOFCluster; // store for each tpc track index the index to the TOF cluster // get matches to TOF in case skimmed data is produced if (mUnbinnedWriter) { // getLTIntegralOut(), ///< L,TOF integral calculated during the propagation // getSignal() mSignal = 0.0; ///< TOF time in ps o2::track::TrackLTIntegral defLT; - idxTPCTrackToTOFCluster = std::vector>(tracksTPC.size(), {-1, -999, -999, defLT, 0, 0, 0}); + idxTPCTrackToTOFCluster = std::vector>(tracksTPC.size(), {-1, -999, -999, defLT, 0, 0, 0, 0}); const std::vector> tofMatches{recoData.getTPCTOFMatches(), recoData.getTPCTRDTOFMatches(), recoData.getITSTPCTOFMatches(), recoData.getITSTPCTRDTOFMatches()}; const auto& ft0rec = recoData.getFT0RecPoints(); @@ -286,7 +289,7 @@ class TPCTimeSeries : public Task mask |= o2::dataformats::MatchInfoTOF::QualityFlags::hasT0_1BCbefore; } - idxTPCTrackToTOFCluster[refTPC] = {tpctofmatch.getIdxTOFCl(), tpctofmatch.getDXatTOF(), tpctofmatch.getDZatTOF(), ltIntegral, signal, deltaT, mask}; + idxTPCTrackToTOFCluster[refTPC] = {tpctofmatch.getIdxTOFCl(), tpctofmatch.getDXatTOF(), tpctofmatch.getDZatTOF(), ltIntegral, signal, deltaT, mask, tpctofmatch.getChannel() % 8736}; } } } @@ -870,6 +873,7 @@ class TPCTimeSeries : public Task void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { mTPCVDriftHelper.accountCCDBInputs(matcher, obj); + mPTHelper.accountCCDBInputs(matcher, obj); o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); } @@ -1107,6 +1111,7 @@ class TPCTimeSeries : public Task long mTimeMS{}; ///< time in MS of current TF int mRun{}; ///< run number int mMaxOccupancyHistBins{912}; ///< maximum number of occupancy bins + PressureTemperatureHelper mPTHelper; ///< helper to extract pressure and temperature from CCDB /// check if track passes coarse cuts bool acceptTrack(const TrackTPC& track) const { return std::abs(track.getTgl()) < mMaxTgl; } @@ -1117,7 +1122,7 @@ class TPCTimeSeries : public Task return isGoodTrack; } - void fillDCA(const gsl::span tracksTPC, const gsl::span tracksITSTPC, const gsl::span vertices, const int iTrk, const int iThread, const std::unordered_map>& indicesITSTPC, const gsl::span tracksITS, const std::vector>& idxTPCTrackToTOFCluster, const gsl::span tofClusters) + void fillDCA(const gsl::span tracksTPC, const gsl::span tracksITSTPC, const gsl::span vertices, const int iTrk, const int iThread, const std::unordered_map>& indicesITSTPC, const gsl::span tracksITS, const std::vector>& idxTPCTrackToTOFCluster, const gsl::span tofClusters) { const auto& trackFull = tracksTPC[iTrk]; const bool isGoodTrack = checkTrack(trackFull); @@ -1507,6 +1512,7 @@ class TPCTimeSeries : public Task << "vertexTime=" << vertexTime /// time stamp assigned to the vertex << "trackTime0=" << trackTime0 /// time stamp assigned to the track << "TOFmask=" << std::get<6>(idxTPCTrackToTOFCluster[iTrk]) /// delta T- TPC TOF + << "TOFchannel=" << std::get<7>(idxTPCTrackToTOFCluster[iTrk]) /// TOF channel inside a sector // TPC delta param << "deltaTPCParamInOutTgl=" << deltaTPCParamInOutTgl << "deltaTPCParamInOutQPt=" << deltaTPCParamInOutQPt @@ -1823,12 +1829,11 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(GTrackID::getSourcesMask("TPC"), useMC); - dataRequest->requestFT0RecPoints(false); - bool tpcOnly = srcTracks == GTrackID::getSourcesMask("TPC"); if (!tpcOnly) { - dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestFT0RecPoints(useMC); } + dataRequest->requestPrimaryVertices(useMC); const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT; auto ccdbRequest = std::make_shared(!disableWriter, // orbitResetTime @@ -1842,6 +1847,7 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + PressureTemperatureHelper::requestCCDBInputs(dataRequest->inputs); std::vector outputs; outputs.emplace_back(o2::header::gDataOriginTPC, getDataDescriptionTimeSeries(), 0, Lifetime::Sporadic); if (!disableWriter) { diff --git a/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx b/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx index 957ed07078cfe..8456fa3fa4740 100644 --- a/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCVDriftTglCalibSpec.cxx @@ -20,6 +20,7 @@ #include "CCDB/CcdbApi.h" #include "CCDB/CcdbObjectInfo.h" #include "Framework/Task.h" +#include "TPCCalibration/PressureTemperatureHelper.h" using namespace o2::framework; @@ -52,12 +53,24 @@ class TPCVDriftTglCalibSpec : public Task return; } o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mPTHelper.extractCCDBInputs(pc); auto data = pc.inputs().get>>("input"); o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); if (data.size()) { LOG(detail) << "Processing TF " << mCalibrator->getCurrentTFInfo().tfCounter << " with " << data.size() - 2 << " tracks"; // 1st entry is for VDrift, 2nd for the offset } - mCalibrator->process(data); + + // if no T/P scaling of the VDrift was performed get the current T/P + float tp = 0; + if (!data.empty()) { + // third value of first entry is the T/P ratio, if it is 0, we use the current T/P + if (data[0].third == 0) { + const auto timestamp = pc.services().get().creation; + tp = mPTHelper.getTP(timestamp); + } + } + + mCalibrator->process(data, tp); if (pc.transitionState() == TransitionHandlingState::Requested) { LOG(info) << "Run stop requested, finalizing"; mRunStopRequested = true; @@ -80,13 +93,15 @@ class TPCVDriftTglCalibSpec : public Task void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + mPTHelper.accountCCDBInputs(matcher, obj); } private: void sendOutput(DataAllocator& output); std::unique_ptr mCalibrator; std::shared_ptr mCCDBRequest; - bool mRunStopRequested = false; // flag that run was stopped (ant the last output is sent) + PressureTemperatureHelper mPTHelper; // helper to extract pressure and temperature from CCDB + bool mRunStopRequested = false; // flag that run was stopped (ant the last output is sent) }; //_____________________________________________________________ @@ -134,6 +149,8 @@ DataProcessorSpec getTPCVDriftTglCalibSpec(int ntgl, float tglMax, int ndtgl, fl outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TPCVDTGL"}, Lifetime::Sporadic); slot0frac = 1. - slot0frac; + PressureTemperatureHelper::requestCCDBInputs(inputs); + return DataProcessorSpec{ "tpc-vd-tgl-calib", inputs, diff --git a/Detectors/TPC/workflow/src/ZSSpec.cxx b/Detectors/TPC/workflow/src/ZSSpec.cxx index ccd59de42f000..c24647f6ae240 100644 --- a/Detectors/TPC/workflow/src/ZSSpec.cxx +++ b/Detectors/TPC/workflow/src/ZSSpec.cxx @@ -22,7 +22,7 @@ #include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTPC/Helpers.h" #include "DataFormatsTPC/Digit.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUHostDataTypes.h" #include "GPUO2InterfaceConfiguration.h" #include "TPCBase/Sector.h" diff --git a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx index 3f9029cf384a9..c09eb193e0fbf 100644 --- a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TPC/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, "ccdb", {"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"}}, ConfigParamSpec{"inputFromFile", VariantType::Bool, false, {"Expect COMPCLUSTERS from file"}}}; @@ -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::tpc::getEntropyEncoderSpec(cfgc.options().get("inputFromFile"), cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::tpc::getEntropyEncoderSpec(cfgc.options().get("inputFromFile"), cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx b/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx deleted file mode 100644 index c17b68e307328..0000000000000 --- a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TPCWorkflow/TPCMergeTimeSeriesSpec.h" -#include "CommonUtils/ConfigurableParam.h" -#include "Framework/ConfigParamSpec.h" - -using namespace o2::framework; - -void customize(std::vector& workflowOptions) -{ - std::vector options{ - ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - }; - std::swap(workflowOptions, options); -} - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - WorkflowSpec wf; - o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::tpc::getTPCMergeTimeSeriesSpec()); - return wf; -} diff --git a/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx b/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx index 112e8ff2cd3a4..ae05b0c431626 100644 --- a/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx +++ b/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx @@ -13,6 +13,10 @@ #include "TPCWorkflow/MIPTrackFilterSpec.h" #include "Framework/ConfigParamSpec.h" #include "DataFormatsTPC/TrackTPC.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +using GID = o2::dataformats::GlobalTrackID; template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; @@ -21,6 +25,8 @@ void customize(std::vector& workflowOptions) { std::vector options{ {"enable-writer", VariantType::Bool, false, {"selection string input specs"}}, + {"use-global-tracks", VariantType::Bool, false, {"use global matched tracks instead of TPC only"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, }; std::swap(workflowOptions, options); @@ -33,8 +39,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) { using namespace o2::tpc; + const auto useGlobal = config.options().get("use-global-tracks"); WorkflowSpec workflow; - workflow.emplace_back(getMIPTrackFilterSpec()); + + const auto useMC = false; + auto srcTracks = GID::getSourcesMask("TPC"); + const auto srcCls = GID::getSourcesMask(""); + if (useGlobal) { + srcTracks = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + } + + workflow.emplace_back(getMIPTrackFilterSpec(srcTracks)); if (config.options().get("enable-writer")) { const char* processName = "tpc-mips-writer"; diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 9d7ab63b0c2a0..07b1c293bff98 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -57,8 +57,8 @@ void customize(std::vector& workflowOptions) using namespace o2::framework; std::vector options{ - {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters, compressed-clusters-ctf, pass-through"}}, - {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map, tpc-triggers"}}, + {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters-root, compressed-clusters-flat, compressed-clusters-flat-for-encode, pass-through"}}, + {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters-root, compressed-clusters-flat, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map, tpc-triggers"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"no-ca-clusterer", VariantType::Bool, false, {"Use HardwareClusterer instead of clusterer of GPUCATracking"}}, {"disable-mc", VariantType::Bool, false, {"disable sending of MC information"}}, @@ -71,6 +71,7 @@ void customize(std::vector& workflowOptions) {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, {"filtered-input", VariantType::Bool, false, {"Filtered tracks, clusters input, prefix dataDescriptors with F"}}, {"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}, + {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, {"tpc-deadMap-sources", VariantType::Int, -1, {"Sources to consider for TPC dead channel map creation; -1=all, 0=deactivated"}}, {"tpc-mc-time-gain", VariantType::Bool, false, {"use time gain calibration for MC (true) or for data (false)"}}, }; @@ -155,8 +156,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "DIGITS"}; } else if (inputType == "clustershw") { gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERHW"}; - } else if (inputType == "clustersnative") { - gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}; } else if (inputType == "zsraw") { gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "RAWDATA"}; } @@ -184,6 +183,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) !cfgc.options().get("no-ca-clusterer"), // !cfgc.options().get("no-tpc-zs-on-the-fly"), // !cfgc.options().get("ignore-dist-stf"), // + cfgc.options().get("ctf-dict"), cfgc.options().get("select-ir-frames"), cfgc.options().get("filtered-input"), cfgc.options().get("tpc-deadMap-sources"), diff --git a/Detectors/TRD/base/CMakeLists.txt b/Detectors/TRD/base/CMakeLists.txt index 030fb6cea1e50..e0563a85a3f42 100644 --- a/Detectors/TRD/base/CMakeLists.txt +++ b/Detectors/TRD/base/CMakeLists.txt @@ -16,7 +16,6 @@ o2_add_library(TRDBase src/GeometryFlat.cxx src/PadResponse.cxx src/FeeParam.cxx - src/RecoParam.cxx src/ChamberStatus.cxx src/Calibrations.cxx src/CalOnlineGainTables.cxx @@ -38,7 +37,6 @@ o2_target_root_dictionary(TRDBase include/TRDBase/GeometryFlat.h include/TRDBase/PadResponse.h include/TRDBase/FeeParam.h - include/TRDBase/RecoParam.h include/TRDBase/Calibrations.h include/TRDBase/PadParameters.h include/TRDBase/PadCalibrations.h diff --git a/Detectors/TRD/base/include/TRDBase/RecoParam.h b/Detectors/TRD/base/include/TRDBase/RecoParam.h deleted file mode 100644 index 1828a0b1724e9..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/RecoParam.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoParam.h -/// \brief Error parameterizations and helper functions for TRD reconstruction -/// \author Ole Schmidt - -#ifndef O2_TRD_RECOPARAM_H -#define O2_TRD_RECOPARAM_H - -#include -#include "Rtypes.h" - -namespace o2 -{ -namespace trd -{ - -class RecoParam -{ - public: - RecoParam() = default; - RecoParam(const RecoParam&) = default; - ~RecoParam() = default; - - /// Load parameterization for given magnetic field - void setBfield(float bz); - - /// Recalculate tracklet covariance based on phi angle of related track - void recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array& cov) const; - - /// Get tracklet r-phi resolution for given phi angle - /// Resolution depends on the track angle sin(phi) = snp and is approximated by the formula - /// sigma_y(snp) = sqrt(a^2 + c^2 * (snp - b^2)^2) - /// more details are given in http://cds.cern.ch/record/2724259 in section 5.3.3 - /// \param phi angle of related track - /// \return sigma_y^2 of tracklet - float getRPhiRes(float snp) const { return (mA2 + mC2 * (snp - mB) * (snp - mB)); } - - /// Get tracklet z correction coefficient for track-eta based corraction - float getZCorrCoeffNRC() const { return mZCorrCoefNRC; } - - private: - // tracklet error parameterization depends on the magnetic field - float mA2{1.f}; ///< parameterization for tracklet position resolution - float mB{0.f}; ///< parameterization for tracklet position resolution - float mC2{0.f}; ///< parameterization for tracklet position resolution - float mZCorrCoefNRC{1.4f}; ///< tracklet z-position depends linearly on track dip angle - - ClassDefNV(RecoParam, 1); -}; - -} // namespace trd -} // namespace o2 - -#endif // O2_TRD_RECOPARAM_H diff --git a/Detectors/TRD/base/macros/OCDB2CCDB.C b/Detectors/TRD/base/macros/OCDB2CCDB.C index f7723089bd5a6..35cf86d05d22f 100644 --- a/Detectors/TRD/base/macros/OCDB2CCDB.C +++ b/Detectors/TRD/base/macros/OCDB2CCDB.C @@ -266,7 +266,7 @@ void OCDB2CCDB(long timeStamp = -1, TString ccdbPath = "http://localhost:8080", //Connect to CCDB // o2::ccdb::CcdbApi ccdb; - map metadata; // do we want to store any meta data? + std::map metadata; // do we want to store any meta data? ccdb.init(ccdbPath.Data()); AliTRDCalChamberStatus* chamberStatus = 0; diff --git a/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C b/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C index 0b4d93906efb5..edf8bfff15129 100644 --- a/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C +++ b/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C @@ -206,7 +206,7 @@ void OCDB2CCDBTrapConfig(TString ccdbPath = "http://localhost:8080", Int_t run = //Connect to CCDB // o2::ccdb::CcdbApi ccdb; - map metadata; // do we want to store any meta data? + std::map metadata; // do we want to store any meta data? metadata.emplace(std::make_pair("UploadedBy", "marten")); metadata.emplace(std::make_pair("Description", "Default TRAP config for Run 3 simulations in LS2")); ccdb.init(ccdbPath.Data()); diff --git a/Detectors/TRD/base/macros/PrintTrapConfig.C b/Detectors/TRD/base/macros/PrintTrapConfig.C index b9b0c3226dcc1..5ed22c32d45aa 100644 --- a/Detectors/TRD/base/macros/PrintTrapConfig.C +++ b/Detectors/TRD/base/macros/PrintTrapConfig.C @@ -236,7 +236,7 @@ void PrintTrapConfig(Int_t run, const Char_t* storageURI = "alien://folder=/alic //Connect to CCDB // o2::ccdb::CcdbApi ccdb; - map metadata; // do we want to store any meta data? + std::map metadata; // do we want to store any meta data? ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation /* diff --git a/Detectors/TRD/base/macros/Readocdb.C b/Detectors/TRD/base/macros/Readocdb.C index 55bea0c2e9cf2..4839f11a41590 100644 --- a/Detectors/TRD/base/macros/Readocdb.C +++ b/Detectors/TRD/base/macros/Readocdb.C @@ -251,7 +251,7 @@ void Readocdb(Int_t run, const Char_t* storageURI = "alien://folder=/alice/data/ //Connect to CCDB // o2::ccdb::CcdbApi ccdb; - map metadata; // do we want to store any meta data? + std::map metadata; // do we want to store any meta data? ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation AliTRDCalChamberStatus* chamberStatus = 0; diff --git a/Detectors/TRD/base/src/CalSingleChamberStatus.cxx b/Detectors/TRD/base/src/CalSingleChamberStatus.cxx deleted file mode 100644 index f054d49766461..0000000000000 --- a/Detectors/TRD/base/src/CalSingleChamberStatus.cxx +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/////////////////////////////////////////////////////////////////////////////// -// // -// Calibration base class for a single ROC // -// Contains one char value per pad // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include "TRDBase/CalSingleChamberStatus.h" - -using namespace o2::trd; - -//_____________________________________________________________________________ -CalSingleChamberStatus::CalSingleChamberStatus() = default; - -//_____________________________________________________________________________ -CalSingleChamberStatus::CalSingleChamberStatus(Int_t p, Int_t c, Int_t cols) - : mPla(p), mCha(c), mNcols(cols) -{ - // - // Constructor that initializes a given pad plane type - // - - // - // The pad plane parameter - // - switch (p) { - case 0: - if (c == 2) { - // L0C0 type - mNrows = 12; - } else { - // L0C1 type - mNrows = 16; - } - break; - case 1: - if (c == 2) { - // L1C0 type - mNrows = 12; - } else { - // L1C1 type - mNrows = 16; - } - break; - case 2: - if (c == 2) { - // L2C0 type - mNrows = 12; - } else { - // L2C1 type - mNrows = 16; - } - break; - case 3: - if (c == 2) { - // L3C0 type - mNrows = 12; - } else { - // L3C1 type - mNrows = 16; - } - break; - case 4: - if (c == 2) { - // L4C0 type - mNrows = 12; - } else { - // L4C1 type - mNrows = 16; - } - break; - case 5: - if (c == 2) { - // L5C0 type - mNrows = 12; - } else { - // L5C1 type - mNrows = 16; - } - break; - }; - - mNchannels = mNrows * mNcols; - if (mNchannels != 0) { - mData.resize(mNchannels); - } - memset(&mData[0], 0, sizeof(mData[0]) * mData.size()); -} - -//_____________________________________________________________________________ -CalSingleChamberStatus::CalSingleChamberStatus(const CalSingleChamberStatus& c) - : mPla(c.mPla), mCha(c.mCha), mNrows(c.mNrows), mNcols(c.mNcols), mNchannels(c.mNchannels) -{ - // - // CalSingleChamberStatus copy constructor - // - - mData = c.mData; -} - -//_____________________________________________________________________________ -CalSingleChamberStatus::~CalSingleChamberStatus() = default; - -//_____________________________________________________________________________ -CalSingleChamberStatus& CalSingleChamberStatus::operator=(const CalSingleChamberStatus& c) -{ - // - // Assignment operator - // - - if (this == &c) { - return *this; - } - - mPla = c.mPla; - mCha = c.mCha; - mNrows = c.mNrows; - mNcols = c.mNcols; - mNchannels = c.mNchannels; - mData = c.mData; - - return *this; -} - -//_____________________________________________________________________________ -void CalSingleChamberStatus::Copy(CalSingleChamberStatus& c) const -{ - // - // Copy function - // - - Int_t iBin = 0; - - c.mPla = mPla; - c.mCha = mCha; - - c.mNrows = mNrows; - c.mNcols = mNcols; - - c.mNchannels = mNchannels; - - c.mData = mData; -} diff --git a/Detectors/TRD/base/src/RecoParam.cxx b/Detectors/TRD/base/src/RecoParam.cxx deleted file mode 100644 index 34921777bdb72..0000000000000 --- a/Detectors/TRD/base/src/RecoParam.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. - -/// \file RecoParam.cxx -/// \brief Error parameterizations and helper functions for TRD reconstruction -/// \author Ole Schmidt - -#include "TRDBase/RecoParam.h" -#include -#include - -using namespace o2::trd; - -// error parameterizations taken from http://cds.cern.ch/record/2724259 Appendix A -void RecoParam::setBfield(float bz) -{ - if (std::fabs(std::fabs(bz) - 2) < 0.1) { - if (bz > 0) { - // magnetic field +0.2 T - mA2 = 1.6e-3f; - mB = -1.43e-2f; - mC2 = 4.55e-2f; - } else { - // magnetic field -0.2 T - mA2 = 1.6e-3f; - mB = 1.43e-2f; - mC2 = 4.55e-2f; - } - } else if (std::fabs(std::fabs(bz) - 5) < 0.1) { - if (bz > 0) { - // magnetic field +0.5 T - mA2 = 1.6e-3f; - mB = 0.125f; - mC2 = 0.0961f; - } else { - // magnetic field -0.5 T - mA2 = 1.6e-3f; - mB = -0.14f; - mC2 = 0.1156f; - } - } else { - LOG(warning) << "No error parameterization available for Bz= " << bz << ". Keeping default value (sigma_y = const. = 1cm)"; - } - LOG(info) << "Loaded error parameterization for Bz = " << bz; -} - -void RecoParam::recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array& cov) const -{ - float t2 = tilt * tilt; // tan^2 (tilt) - float c2 = 1.f / (1.f + t2); // cos^2 (tilt) - float sy2 = getRPhiRes(snp); - float sz2 = rowSize * rowSize / 12.f; - cov[0] = c2 * (sy2 + t2 * sz2); - cov[1] = c2 * tilt * (sz2 - sy2); - cov[2] = c2 * (t2 * sy2 + sz2); -} diff --git a/Detectors/TRD/base/src/TRDBaseLinkDef.h b/Detectors/TRD/base/src/TRDBaseLinkDef.h index 2d3de311a1dc0..a835def5628b2 100644 --- a/Detectors/TRD/base/src/TRDBaseLinkDef.h +++ b/Detectors/TRD/base/src/TRDBaseLinkDef.h @@ -19,7 +19,6 @@ #pragma link C++ class o2::trd::Geometry + ; #pragma link C++ class o2::trd::GeometryBase + ; #pragma link C++ class o2::trd::FeeParam + ; -#pragma link C++ class o2::trd::RecoParam + ; #pragma link C++ class o2::trd::PadResponse + ; #pragma link C++ class o2::trd::PadParameters < float > +; #pragma link C++ class o2::trd::PadParameters < char> + ; diff --git a/Detectors/TRD/calibration/CMakeLists.txt b/Detectors/TRD/calibration/CMakeLists.txt index 36d00e92bbc16..52444d2855b1f 100644 --- a/Detectors/TRD/calibration/CMakeLists.txt +++ b/Detectors/TRD/calibration/CMakeLists.txt @@ -28,6 +28,7 @@ o2_add_library(TRDCalibration O2::DetectorsBase O2::DetectorsCalibration O2::MathUtils + O2::GPUTracking O2::DetectorsDCS) o2_target_root_dictionary(TRDCalibration diff --git a/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h b/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h index 27cba85a89941..8e4a99e5e85d3 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h @@ -96,7 +96,7 @@ class DCSProcessor const std::unordered_map& getTRDCurrentsDPsInfo() const { return mTRDDCSCurrents; } const std::unordered_map& getTRDEnvDPsInfo() const { return mTRDDCSEnv; } const std::array& getTRDFedChamberStatusDPsInfo() const { return mTRDDCSFedChamberStatus; } - const std::array& getTRDFedCFGtagDPsInfo() const { return mTRDDCSFedCFGtag; } + const std::array& getTRDFedCFGtagDPsInfo() const { return mTRDDCSFedCFGtag; } // settings void setCurrentTS(TFType tf) { mCurrentTS = tf; } @@ -124,7 +124,7 @@ class DCSProcessor std::unordered_map mTRDDCSVoltages; ///< anode and drift voltages std::unordered_map mTRDDCSEnv; ///< environment parameters (temperatures, pressures, humidity) std::array mTRDDCSFedChamberStatus; ///< fed chamber status - std::array mTRDDCSFedCFGtag; ///< fed config tag + std::array mTRDDCSFedCFGtag; ///< fed config tag // helper variables std::unordered_map mPids; ///< flag for each DP whether it has been processed at least once diff --git a/Detectors/TRD/calibration/include/TRDCalibration/PulseHeight.h b/Detectors/TRD/calibration/include/TRDCalibration/PulseHeight.h index 3fc70603da7d5..52305cc585b34 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/PulseHeight.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/PulseHeight.h @@ -61,6 +61,7 @@ class PulseHeight /// Access to output const std::vector& getPHData() { return mPHValues; } + const std::vector& getPHDataHD() { return mPHValuesHD; } void createOutputFile(); void closeOutputFile(); @@ -77,6 +78,7 @@ class PulseHeight // output std::vector mPHValues, *mPHValuesPtr{&mPHValues}; ///< vector of values used to fill the PH spectra per detector + std::vector mPHValuesHD, *mPHValuesHDPtr{&mPHValuesHD}; ///< vector of values used to fill the High definition PH spectra per detector with pretrigger phase std::vector mDistances, *mDistancesPtr{&mDistances}; ///< pad distance between tracklet column and digit ADC maximum std::unique_ptr mOutFile{nullptr}; ///< output file std::unique_ptr mOutTree{nullptr}; ///< output tree diff --git a/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h b/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h index 49ba9fdf3d161..7249016d9675e 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h @@ -24,7 +24,7 @@ #include "DataFormatsTRD/NoiseCalibration.h" #include "TRDBase/PadCalibrationsAliases.h" #include "DetectorsBase/Propagator.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" #include "Rtypes.h" @@ -90,7 +90,7 @@ class TrackBasedCalib float mMaxSnp{o2::base::Propagator::MAX_SIN_PHI}; ///< max snp when propagating tracks float mMaxStep{o2::base::Propagator::MAX_STEP}; ///< maximum step for propagation MatCorrType mMatCorr{MatCorrType::USEMatCorrNONE}; ///< if material correction should be done - RecoParam mRecoParam; ///< parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD reconstruction AngularResidHistos mAngResHistos; ///< aggregated data for the track based calibration std::vector mGainCalibHistos; ///< aggregated input data for gain calibration float bz; ///< magnetic field diff --git a/Detectors/TRD/calibration/src/DCSProcessor.cxx b/Detectors/TRD/calibration/src/DCSProcessor.cxx index 165bbbd6a6148..f110ba844791e 100644 --- a/Detectors/TRD/calibration/src/DCSProcessor.cxx +++ b/Detectors/TRD/calibration/src/DCSProcessor.cxx @@ -120,7 +120,7 @@ int DCSProcessor::processDP(const DPCOM& dpcom) } else if (type == DPVAL_INT) { LOG(info) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue(dpcom); } else if (type == DPVAL_STRING) { - LOG(info) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue(dpcom); + LOG(info) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue(dpcom); } } auto flags = dpcom.data.get_flags(); @@ -265,15 +265,15 @@ int DCSProcessor::processDP(const DPCOM& dpcom) int chamberId = getChamberIdFromAlias(dpid.get_alias()); auto& dpInfoFedCFGtag = mTRDDCSFedCFGtag[chamberId]; if (etime != mLastDPTimeStamps[dpid]) { - if (dpInfoFedCFGtag != o2::dcs::getValue(dpcom)) { + if (dpInfoFedCFGtag != o2::dcs::getValue(dpcom)) { // If value changes after processing and DPs should not be updated, log change as warning (for now) if (mPids[dpid] && !(mFedCFGtagCompleteDPs && mFirstRunEntryForFedCFGtagUpdate)) { // Issue an alarm if counter is lower than maximum, warning otherwise if (mFedCFGtagAlarmCounter < mFedAlarmCounterMax) { - LOG(alarm) << "CFGtag change " << dpid.get_alias() << " : " << dpInfoFedCFGtag << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; + LOG(alarm) << "CFGtag change " << dpid.get_alias() << " : " << dpInfoFedCFGtag << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; mFedCFGtagAlarmCounter++; } else if (mVerbosity > 0) { - LOG(warn) << "CFGtag change " << dpid.get_alias() << " : " << dpInfoFedCFGtag << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; + LOG(warn) << "CFGtag change " << dpid.get_alias() << " : " << dpInfoFedCFGtag << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; } } } diff --git a/Detectors/TRD/calibration/src/PulseHeight.cxx b/Detectors/TRD/calibration/src/PulseHeight.cxx index 1981fe528f0f5..44446d40df438 100644 --- a/Detectors/TRD/calibration/src/PulseHeight.cxx +++ b/Detectors/TRD/calibration/src/PulseHeight.cxx @@ -23,6 +23,7 @@ using namespace o2::trd::constants; void PulseHeight::reset() { mPHValues.clear(); + mPHValuesHD.clear(); mDistances.clear(); } @@ -39,6 +40,7 @@ void PulseHeight::createOutputFile() } mOutTree = std::make_unique("ph", "Data points for PH histograms"); mOutTree->Branch("values", &mPHValuesPtr); + mOutTree->Branch("valuesHD", &mPHValuesHDPtr); mOutTree->Branch("dist", &mDistancesPtr); mWriteOutput = true; LOG(info) << "Writing PH data points to local file trd_PH.root"; @@ -178,13 +180,17 @@ void PulseHeight::findDigitsForTracklet(const Tracklet64& trklt, const TriggerRe mDistances.push_back(digitTrackletDistance); for (int iTb = 0; iTb < TIMEBINS; ++iTb) { uint16_t phVal = digit.getADC()[iTb]; + uint16_t phValHD = (digit.getADC()[iTb] << 2) + digit.getPreTrigPhase(); if (left) { phVal += digitLeft->getADC()[iTb]; + phValHD += (digitLeft->getADC()[iTb] << 2) + digit.getPreTrigPhase(); } if (right) { phVal += digitRight->getADC()[iTb]; + phValHD += (digitRight->getADC()[iTb] << 2) + digit.getPreTrigPhase(); } mPHValues.emplace_back(phVal, trkltDet, iTb, nNeighbours, type); + mPHValuesHD.emplace_back(phValHD, trkltDet, iTb, nNeighbours, type); } } } diff --git a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx index 011a888a47618..8fe195f861389 100644 --- a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx +++ b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx @@ -13,6 +13,7 @@ /// \brief Provides information required for TRD calibration which is based on the global tracking /// \author Ole Schmidt +#include "GPUO2InterfaceConfiguration.h" #include "TRDCalibration/TrackBasedCalib.h" #include "TRDCalibration/CalibrationParams.h" #include "DataFormatsTRD/Constants.h" @@ -35,7 +36,9 @@ void TrackBasedCalib::reset() void TrackBasedCalib::init() { bz = o2::base::Propagator::Instance()->getNominalBz(); - mRecoParam.setBfield(bz); + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(bz, &config.configReconstruction); } void TrackBasedCalib::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/qc/CMakeLists.txt b/Detectors/TRD/qc/CMakeLists.txt index d631de1f54246..daba4928957f9 100644 --- a/Detectors/TRD/qc/CMakeLists.txt +++ b/Detectors/TRD/qc/CMakeLists.txt @@ -21,6 +21,7 @@ o2_add_library(TRDQC O2::DataFormatsTRD O2::DataFormatsGlobalTracking O2::DetectorsBase + O2::GPUTracking O2::MathUtils) o2_target_root_dictionary(TRDQC diff --git a/Detectors/TRD/qc/include/TRDQC/Tracking.h b/Detectors/TRD/qc/include/TRDQC/Tracking.h index 880b1727ab367..f39c64286d0cc 100644 --- a/Detectors/TRD/qc/include/TRDQC/Tracking.h +++ b/Detectors/TRD/qc/include/TRDQC/Tracking.h @@ -25,7 +25,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsTPC/TrackTPC.h" #include "DetectorsBase/Propagator.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" #include "Rtypes.h" #include "TH1.h" @@ -107,7 +107,7 @@ class Tracking float mMaxSnp{o2::base::Propagator::MAX_SIN_PHI}; ///< max snp when propagating tracks float mMaxStep{o2::base::Propagator::MAX_STEP}; ///< maximum step for propagation MatCorrType mMatCorr{MatCorrType::USEMatCorrNONE}; ///< if material correction should be done - RecoParam mRecoParam; ///< parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD reconstruction bool mPID{true}; ///< if TPC only tracks are not available we don't fill PID info bool mApplyShift{true}; diff --git a/Detectors/TRD/qc/src/RawDataManager.cxx b/Detectors/TRD/qc/src/RawDataManager.cxx index 1add53cae12ac..c53cd434e6b7b 100644 --- a/Detectors/TRD/qc/src/RawDataManager.cxx +++ b/Detectors/TRD/qc/src/RawDataManager.cxx @@ -23,6 +23,8 @@ #include #include +#include +#include using namespace o2::trd; diff --git a/Detectors/TRD/qc/src/Tracking.cxx b/Detectors/TRD/qc/src/Tracking.cxx index 278ebe5391ff9..9a0df7efa323b 100644 --- a/Detectors/TRD/qc/src/Tracking.cxx +++ b/Detectors/TRD/qc/src/Tracking.cxx @@ -13,6 +13,7 @@ /// \brief Check the performance of the TRD in global tracking /// \author Ole Schmidt +#include "GPUO2InterfaceConfiguration.h" #include "TRDQC/Tracking.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DetectorsBase/GeometryManager.h" @@ -25,7 +26,9 @@ using namespace o2::trd::constants; void Tracking::init() { - mRecoParam.setBfield(o2::base::Propagator::Instance()->getNominalBz()); + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz(), &config.configReconstruction); } void Tracking::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index 107a05a397c00..adb584ef15ec4 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -33,10 +33,10 @@ namespace o2 namespace trd { -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::TRD) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TRD, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -259,8 +259,8 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VTRK& tr uint32_t firstEntryDig = digVec.size(); int16_t cid = 0; for (uint32_t id = 0; id < entriesDig[itrig]; id++) { - cid += CIDDig[digCount]; // 1st digit of trigger was encoded with abs CID, then increments - auto& dig = digVec.emplace_back(cid, ROBDig[digCount], MCMDig[digCount], chanDig[digCount]); + cid += CIDDig[digCount]; // as cid has phase, its stored fully not // 1st digit of trigger was encoded with abs CID, then increments + auto& dig = digVec.emplace_back(cid & 0xfff, ROBDig[digCount], MCMDig[digCount], chanDig[digCount], (cid >> 12) & 0x3); dig.setADC({&ADCDig[adcCount], constants::TIMEBINS}); digCount++; adcCount += constants::TIMEBINS; diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFHelper.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFHelper.h index 316f2c8a5c7f0..bb41ea9658c9d 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFHelper.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFHelper.h @@ -288,12 +288,12 @@ class CTFHelper // assume sorting in CID: for the 1st digit of the trigger return the abs CID, for the following ones: difference to previous CID value_type operator*() const { - return (*mTrigStart)[mIndex] ? mData[mIndex].getDetector() : mData[mIndex].getDetector() - mData[mIndex - 1].getDetector(); + return (*mTrigStart)[mIndex] ? mData[mIndex].getDetectorInFull() : mData[mIndex].getDetectorInFull() - mData[mIndex - 1].getDetectorInFull(); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return (*mTrigStart)[id] ? mData[id].getDetector() : mData[id].getDetector() - mData[id - 1].getDetector(); + return (*mTrigStart)[id] ? mData[id].getDetectorInFull() : mData[id].getDetectorInFull() - mData[id - 1].getDetectorInFull(); } }; diff --git a/Detectors/TRD/reconstruction/src/CruRawReader.cxx b/Detectors/TRD/reconstruction/src/CruRawReader.cxx index b4a37956759b9..05666691370db 100644 --- a/Detectors/TRD/reconstruction/src/CruRawReader.cxx +++ b/Detectors/TRD/reconstruction/src/CruRawReader.cxx @@ -301,6 +301,9 @@ bool CruRawReader::parseDigitHCHeaders(int hcid) DigitHCHeader1 header1; header1.word = headers[headerwordcount]; mPreTriggerPhase = header1.ptrigphase; + mPreTriggerPhase &= 0x0f; + mPreTriggerPhase /= 3; // remove the "gaps" in the pre trigger phase, so we dont have to sort it out later. + LOGP(debug, "Found pretrigger phase of Phase:{:x}", mPreTriggerPhase); headersfound.set(0); if ((header1.numtimebins > TIMEBINS) || (header1.numtimebins < 3) || mTimeBinsFixed && header1.numtimebins != mTimeBins) { @@ -802,6 +805,7 @@ int CruRawReader::parseDigitLinkData(int maxWords32, int hcid, int& wordsRejecte if (exitChannelLoop) { break; } + LOGP(debug, "Adding digit to event record det: {} rob: {} mcm: {} channel: {} Phase:{:x}", hcid / 2, (int)mcmHeader.rob, (int)mcmHeader.mcm, iChannel, mPreTriggerPhase); mEventRecords.getCurrentEventRecord().addDigit(Digit(hcid / 2, (int)mcmHeader.rob, (int)mcmHeader.mcm, iChannel, adcValues, mPreTriggerPhase)); ++mDigitsFound; } // end active channel diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h index 53c591e343134..9521d6262afbf 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h @@ -24,7 +24,7 @@ namespace trd { /// 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 trd } // namespace o2 diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h index 673b600bee051..e31a629225f2c 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h @@ -24,7 +24,7 @@ namespace trd { /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/GainCalibSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/GainCalibSpec.h index 295ce0bf1d0ac..1c60a05462508 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/GainCalibSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/GainCalibSpec.h @@ -45,7 +45,7 @@ class GainCalibDevice : public o2::framework::Task { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); auto slotL = ic.options().get("sec-per-slot"); - auto delay = ic.options().get("max-delay"); + auto delay = ic.options().get("max-delay"); mCalibrator = std::make_unique(); mCalibrator->setSlotLengthInSeconds(slotL); mCalibrator->setMaxSlotsDelay(delay); @@ -155,7 +155,7 @@ DataProcessorSpec getTRDGainCalibSpec() AlgorithmSpec{adaptFromTask(ccdbRequest)}, Options{ {"sec-per-slot", VariantType::UInt32, 900u, {"number of seconds per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 2u, {"number of slots in past to consider"}}, + {"max-delay", VariantType::Float, 0.05f, {"number of slots in past to consider"}}, {"enable-root-output", VariantType::Bool, false, {"output tprofiles and fits to root file"}}, }}; } diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/T0FitSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/T0FitSpec.h index 3f4fc7a1e69fd..f39174b95ba64 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/T0FitSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/T0FitSpec.h @@ -46,7 +46,7 @@ class T0FitDevice : public o2::framework::Task { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); auto slotL = ic.options().get("sec-per-slot"); - auto delay = ic.options().get("max-delay"); + auto delay = ic.options().get("max-delay"); mFitInstance = std::make_unique(); mFitInstance->setSlotLengthInSeconds(slotL); @@ -159,7 +159,7 @@ DataProcessorSpec getTRDT0FitSpec() AlgorithmSpec{adaptFromTask(ccdbRequest)}, Options{ {"sec-per-slot", VariantType::UInt32, 900u, {"number of seconds per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 2u, {"number of slots in past to consider"}}, + {"max-delay", VariantType::Float, 0.05f, {"number of slots in past to consider"}}, {"enable-root-output", VariantType::Bool, false, {"output t0 values to root file"}}, }}; } diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h index 955af1995b0de..93f07dd58445e 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h @@ -18,8 +18,9 @@ #include "Framework/Task.h" #include "TStopwatch.h" #include "TRDBase/GeometryFlat.h" -#include "GPUO2Interface.h" +#include "GPUO2ExternalUser.h" #include "GPUTRDTracker.h" +#include "GPUTRDRecoParam.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsTRD/TrackTRD.h" @@ -34,7 +35,6 @@ #include "TPCCalibration/CorrectionMapsLoader.h" #include "GPUO2InterfaceRefit.h" #include "TPCFastTransform.h" -#include "TRDBase/RecoParam.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsITS/TrackITS.h" #include "DataFormatsITSMFT/TrkClusRef.h" @@ -57,6 +57,7 @@ class TRDGlobalTracking : public o2::framework::Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TRDGlobalTracking() override = default; void init(o2::framework::InitContext& ic) final; @@ -93,7 +94,7 @@ class TRDGlobalTracking : public o2::framework::Task // temporary members -> should go into processor (GPUTRDTracker or additional refit processor?) std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - RecoParam mRecoParam; ///< parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD reconstruction gsl::span mTrackletsRaw; ///< array of raw tracklets needed for TRD refit gsl::span mTrackletsCalib; ///< array of calibrated tracklets needed for TRD refit gsl::span mTPCTracksArray; ///< input TPC tracks used for refit diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h index 536353c7a9e90..be00e478608ec 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h @@ -58,6 +58,8 @@ class PuseHeightDevice : public o2::framework::Task if (mRunStopRequested) { std::vector mPHValues{}; // the calibration expects data at every TF, so inject dummy pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHT", 0}, mPHValues); + std::vector mPHValuesHD{}; // the calibration expects data at every TF, so inject dummy + pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHTHD", 0}, mPHValuesHD); return; } RecoContainer recoData; @@ -67,6 +69,7 @@ class PuseHeightDevice : public o2::framework::Task mPulseHeight->reset(); mPulseHeight->process(); pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHT", 0}, mPulseHeight->getPHData()); + pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHTHD", 0}, mPulseHeight->getPHDataHD()); if (pc.transitionState() == TransitionHandlingState::Requested) { LOG(info) << "Run stop requested, finalizing"; mRunStopRequested = true; @@ -101,6 +104,7 @@ DataProcessorSpec getTRDPulseHeightSpec(GID::mask_t src, bool digitsFromReader) std::vector outputs; outputs.emplace_back(o2::header::gDataOriginTRD, "PULSEHEIGHT", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTRD, "PULSEHEIGHTHD", 0, Lifetime::Timeframe); bool isTPCavailable = false; if (GID::includesSource(GID::Source::ITSTPC, src)) { diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h index cddbb45e169da..f45b7a1808287 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h @@ -45,7 +45,7 @@ class VdAndExBCalibDevice : public o2::framework::Task { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); auto slotL = ic.options().get("sec-per-slot"); - auto delay = ic.options().get("max-delay"); + auto delay = ic.options().get("max-delay"); mCalibrator = std::make_unique(); mCalibrator->setSlotLengthInSeconds(slotL); mCalibrator->setMaxSlotsDelay(delay); @@ -158,7 +158,7 @@ DataProcessorSpec getTRDVdAndExBCalibSpec() AlgorithmSpec{adaptFromTask(ccdbRequest)}, Options{ {"sec-per-slot", VariantType::UInt32, 900u, {"number of seconds per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 2u, {"number of slots in past to consider"}}, + {"max-delay", VariantType::Float, 0.05f, {"number of slots in past to consider"}}, {"enable-root-output", VariantType::Bool, false, {"output tprofiles and fits to root file"}}, }}; } diff --git a/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx index b30732927c182..2caa4c370a021 100644 --- a/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace trd { - 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; @@ -44,7 +43,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(); @@ -109,7 +108,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"}, "TRD", "TRKTRGRD", 0, Lifetime::Timeframe}, @@ -119,19 +118,20 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_TRD", "TRD", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TRD", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TRD", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "trd-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"}}, - {"correct-trd-trigger-offset", VariantType::Bool, false, {"Correct decoded IR by TriggerOffsetsParam::LM_L0"}}, + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"correct-trd-trigger-offset", VariantType::Bool, false, {"Correct decoded IR by TriggerOffsetsParam::LM_L0"}}, {"bogus-trigger-rejection", VariantType::Int, 10, {">0 : discard, warn N times, <0 : warn only, =0: no check for triggers with no tracklets or bogus IR"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx index d345dd74141ed..18b9a012db2f1 100644 --- a/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace trd { - 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; @@ -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(); @@ -92,13 +91,16 @@ 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", "TRD", "TRKTRGRD", 0, Lifetime::Timeframe); inputs.emplace_back("tracklets", "TRD", "TRACKLETS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "TRD", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -107,14 +109,12 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"TRD", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TRD", "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"}}, {"bogus-trigger-check", VariantType::Int, 10, {"max bogus triggers to report, all if < 0"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index b5a1530e83d8c..f2d4aad829fe5 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -18,6 +18,7 @@ #include "DetectorsBase/GlobalParams.h" #include "DetectorsBase/Propagator.h" #include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/Vertex.h" #include "DataFormatsTRD/Tracklet64.h" #include "DataFormatsTRD/CalibratedTracklet.h" #include "DataFormatsTRD/TriggerRecord.h" @@ -42,10 +43,11 @@ // GPU header #include "GPUReconstruction.h" #include "GPUChainTracking.h" +#include "GPUChainTrackingGetters.inc" #include "GPUO2InterfaceConfiguration.h" #include "GPUO2InterfaceUtils.h" #include "GPUSettings.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTRDDef.h" #include "GPUTRDTrack.h" #include "GPUTRDTrackletWord.h" @@ -102,7 +104,7 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) mFlatGeo = std::make_unique(*geo); GPURecoStepConfiguration cfgRecoStep; - cfgRecoStep.steps = GPUDataTypes::RecoStep::NoRecoStep; + cfgRecoStep.steps = gpudatatypes::RecoStep::NoRecoStep; cfgRecoStep.inputs.clear(); cfgRecoStep.outputs.clear(); mRec = GPUReconstruction::CreateInstance("CPU", true); @@ -111,6 +113,8 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) config.ReadConfigurableParam(config); config.configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*o2::base::GRPGeomHelper::instance().getGRPMagField()); config.configProcessing.o2PropagatorUseGPUField = false; + mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz(), &config.configReconstruction); + mRec->SetSettings(&config.configGRP, &config.configReconstruction, &config.configProcessing, &cfgRecoStep); mChainTracking = mRec->AddChain(); @@ -126,12 +130,11 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) mRec->RegisterGPUProcessor(mTracker, false); mChainTracking->SetTRDGeometry(std::move(mFlatGeo)); + mChainTracking->SetTRDRecoParam(&mRecoParam); if (mRec->Init()) { LOG(fatal) << "GPUReconstruction could not be initialized"; } - mRecoParam.setBfield(o2::base::Propagator::Instance()->getNominalBz()); - mTracker->PrintSettings(); LOG(info) << "Strict matching mode is " << ((mStrict) ? "ON" : "OFF"); LOGF(info, "The search road in time for ITS-TPC tracks is set to %.1f sigma and %.2f us are added to it on top", diff --git a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx index 83fff5bceedef..177f6e4913a26 100644 --- a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TRD/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, "ccdb", {"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::trd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::trd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/Upgrades/ALICE3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/CMakeLists.txt index 0c2bbe5e02a47..0335e85007c01 100644 --- a/Detectors/Upgrades/ALICE3/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -12,10 +12,11 @@ add_subdirectory(Passive) add_subdirectory(TRK) add_subdirectory(ECal) +add_subdirectory(FD3) add_subdirectory(FT3) add_subdirectory(FCT) add_subdirectory(AOD) add_subdirectory(IOTOF) add_subdirectory(RICH) add_subdirectory(MID) -add_subdirectory(macros) \ No newline at end of file +add_subdirectory(macros) diff --git a/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt index 83838a01d13f1..cc0a7b0337619 100644 --- a/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt @@ -10,4 +10,6 @@ # or submit itself to any jurisdiction. add_subdirectory(base) -add_subdirectory(simulation) \ No newline at end of file +add_subdirectory(simulation) +add_subdirectory(reconstruction) +add_subdirectory(DataFormatsECal) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/CMakeLists.txt new file mode 100644 index 0000000000000..3448d6b31029d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/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(DataFormatsECal + SOURCES src/Digit.cxx + SOURCES src/MCLabel.cxx + SOURCES src/Cluster.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::SimulationDataFormat + AliceO2::InfoLogger) + +o2_target_root_dictionary(DataFormatsECal + HEADERS include/DataFormatsECal/Digit.h + HEADERS include/DataFormatsECal/MCLabel.h + HEADERS include/DataFormatsECal/Cluster.h) diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h new file mode 100644 index 0000000000000..4a34ef1679f26 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h @@ -0,0 +1,85 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Cluster.h +/// \brief Definition of ECal cluster class +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_CLUSTER_H +#define ALICEO2_ECAL_CLUSTER_H +#include +#include +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Cluster +{ + public: + Cluster() = default; + Cluster(const Cluster& clu) = default; + ~Cluster() = default; + + // setters + void addDigit(int digitIndex, int towerId, double energy); + void setNLM(int nMax) { mNLM = nMax; } + void setE(float energy) { mE = energy; } + void setX(float x) { mX = x; } + void setY(float y) { mY = y; } + void setZ(float z) { mZ = z; } + void setChi2(float chi2) { mChi2 = chi2; } + void setEdgeFlag(bool isEdge) { mEdge = isEdge; } + void addMcTrackID(int mcTrackID, float energy) { mMcTrackEnergy[mcTrackID] += energy; } + + // getters + const std::map& getMcTrackEnergy() { return mMcTrackEnergy; } + int getMultiplicity() const { return mDigitIndex.size(); } + int getDigitIndex(int i) const { return mDigitIndex[i]; } + int getDigitTowerId(int i) const { return mDigitTowerId[i]; } + float getDigitEnergy(int i) const { return mDigitEnergy[i]; } + float getNLM() const { return mNLM; } + float getTime() const { return mTime; } + float getE() const { return mE; } + float getX() const { return mX; } + float getY() const { return mY; } + float getZ() const { return mZ; } + float getR() const { return std::sqrt(mX * mX + mY * mY); } + float getTheta() const { return std::atan2(getR(), mZ); } + float getEta() const { return -std::log(std::tan(getTheta() / 2.)); } + float getPhi() const { return std::atan2(mY, mX); } + float getChi2() const { return mChi2; } + bool isAtTheEdge() const { return mEdge; } + int getMcTrackID() const; + TLorentzVector getMomentum() const; + + private: + std::vector mDigitIndex; // vector of digit indices in digits vector + std::vector mDigitTowerId; // vector of corresponding digit tower Ids + std::vector mDigitEnergy; // vector of corresponding digit energies + std::map mMcTrackEnergy; // MC track indices and corresponding energies + int mNLM = 0; // number of local maxima in the initial cluster + float mTime = 0; // cluster time + float mE = 0; // cluster energy + float mX = 0; // estimated x-coordinate + float mY = 0; // estimated y-ccordinate + float mZ = 0; // estimated z-ccordinate + float mChi2 = 0; // chi2 wrt EM shape + bool mEdge = 0; // set to true if one of cluster digits is at the chamber edge + ClassDefNV(Cluster, 1); +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_CLUSTER_H diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h new file mode 100644 index 0000000000000..cc46a64e2cac0 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h @@ -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. + +/// \file Digit.h +/// \brief Definition of ECal digit class +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_DIGIT_H +#define ALICEO2_ECAL_DIGIT_H + +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Digit : public o2::dataformats::TimeStamp +{ + public: + Digit() = default; + Digit(int tower, double amplitudeGeV, double time); + ~Digit() = default; + + // setters + void setTower(int tower) { mTower = tower; } + void setAmplitude(double amplitude) { mAmplitudeGeV = amplitude; } + void setEnergy(double energy) { mAmplitudeGeV = energy; } + void setLabel(int label) { mLabel = label; } + + // getters + int getTower() const { return mTower; } + double getAmplitude() const { return mAmplitudeGeV; } + double getEnergy() const { return mAmplitudeGeV; } + int getLabel() const { return mLabel; } + + private: + double mAmplitudeGeV = 0.; ///< Amplitude (GeV) + int32_t mTower = -1; ///< Tower index (absolute cell ID) + int32_t mLabel = -1; ///< Index of the corresponding entry/entries in the MC label array + ClassDefNV(Digit, 1); +}; + +} // namespace ecal +} // namespace o2 +#endif // ALICEO2_ECAL_DIGIT_H diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.h new file mode 100644 index 0000000000000..762779977ca53 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.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. + +/// \file MCLabel.h +/// \brief MCLabel class to store MC truth info for ECal +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_MCLABEL_H +#define ALICEO2_ECAL_MCLABEL_H + +#include + +namespace o2 +{ +namespace ecal +{ +class MCLabel : public o2::MCCompLabel +{ + public: + MCLabel() = default; + MCLabel(int trackID, int eventID, int srcID, bool fake, float edep) : o2::MCCompLabel(trackID, eventID, srcID, fake), mEdep(edep) {} + float getEdep() const { return mEdep; } + + private: + float mEdep = 0; // deposited energy + + ClassDefNV(MCLabel, 1); +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_MCLABEL_H diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx new file mode 100644 index 0000000000000..f792344f1f50f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx @@ -0,0 +1,57 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Cluster.cxx +/// \brief Implementation of ECal cluster class +/// +/// \author Evgeny Kryshen + +#include +#include +#include +#include + +using namespace o2::ecal; + +ClassImp(Cluster); + +//============================================================================== +void Cluster::addDigit(int digitIndex, int towerId, double energy) +{ + mE += energy; + mDigitIndex.push_back(digitIndex); + mDigitTowerId.push_back(towerId); + mDigitEnergy.push_back(energy); +} + +//============================================================================== +int Cluster::getMcTrackID() const +{ + float maxEnergy = 0; + int maxID = 0; + for (const auto& [mcTrackID, energy] : mMcTrackEnergy) { + if (energy > maxEnergy) { + maxEnergy = energy; + maxID = mcTrackID; + } + } + return maxID; +} + +//============================================================================== +TLorentzVector Cluster::getMomentum() const +{ + double r = std::sqrt(mX * mX + mY * mY + mZ * mZ); + if (r == 0) { + return TLorentzVector(); + } + return TLorentzVector(mE * mX / r, mE * mY / r, mE * mZ / r, mE); +} diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h similarity index 52% rename from Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h rename to Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h index a4988b2c18fc7..5b0190aa10d45 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h @@ -9,20 +9,17 @@ // 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 +#ifdef __CLING__ -/// @file FT0Workflow.h +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; -#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 +#pragma link C++ class o2::ecal::Digit + ; +#pragma link C++ class o2::ecal::MCLabel + ; +#pragma link C++ class o2::ecal::Cluster + ; +#pragma link C++ class std::vector < o2::ecal::Digit> + ; +#pragma link C++ class std::vector < o2::ecal::Cluster> + ; +#include "SimulationDataFormat/MCTruthContainer.h" +#pragma link C++ class o2::dataformats::MCTruthContainer < o2::ecal::MCLabel> + ; #endif diff --git a/Framework/TestWorkflows/src/o2_sim_tpc.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx similarity index 62% rename from Framework/TestWorkflows/src/o2_sim_tpc.h rename to Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx index e567fe89e0b38..c339c112c6858 100644 --- a/Framework/TestWorkflows/src/o2_sim_tpc.h +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx @@ -9,17 +9,16 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef WORKFLOWS_O2_SIM_TPC -#define WORKFLOWS_O2_SIM_TPC +/// \file Digit.cxx +/// \brief Implementation of ECal digit class +/// +/// \author Evgeny Kryshen -#include "Framework/DataProcessorSpec.h" +#include -namespace o2 -{ -namespace workflows +using namespace o2::ecal; + +Digit::Digit(int tower, double amplitudeGeV, double time) + : mTower(tower), mAmplitudeGeV(amplitudeGeV), o2::dataformats::TimeStamp(time) { -o2::framework::DataProcessorSpec sim_tpc(); } -} // namespace o2 - -#endif // WORKFLOWS_O2_SIM_TPC diff --git a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx similarity index 73% rename from Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx rename to Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx index 631655d3038ec..4dbd2711f1521 100644 --- a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx @@ -9,16 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// @file RawDataReaderSpec.cxx +/// \file MCLabel.cxx +/// \brief MCLabel class to store MC truth info for ECal +/// +/// \author Evgeny Kryshen -#include "FDDWorkflow/RawDataReaderSpec.h" +#include -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -} // namespace fdd -} // namespace o2 +ClassImp(o2::ecal::MCLabel); diff --git a/Detectors/Upgrades/ALICE3/ECal/README.md b/Detectors/Upgrades/ALICE3/ECal/README.md index 288040fbd5fd9..ff58683646409 100644 --- a/Detectors/Upgrades/ALICE3/ECal/README.md +++ b/Detectors/Upgrades/ALICE3/ECal/README.md @@ -1,10 +1,10 @@ # ALICE 3 Electromagnetic Calorimenter -This is top page for the ECL detector documentation. +This is top page for the ECAL detector documentation. \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt index 70017cc051e80..b0e1229662653 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt @@ -10,10 +10,14 @@ # or submit itself to any jurisdiction. o2_add_library(ECalBase - SOURCES src/GeometryTGeo.cxx + SOURCES src/Geometry.cxx + src/GeometryTGeo.cxx src/ECalBaseParam.cxx + src/Hit.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase) o2_target_root_dictionary(ECalBase - HEADERS include/ECalBase/GeometryTGeo.h - include/ECalBase/ECalBaseParam.h) \ No newline at end of file + HEADERS include/ECalBase/Geometry.h + include/ECalBase/GeometryTGeo.h + include/ECalBase/ECalBaseParam.h + include/ECalBase/Hit.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h index b8b7c75e2b7d0..aa0de4119914a 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h @@ -9,22 +9,45 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file ECalBaseParam.h +/// \brief Geometry parameters configurable via o2-sim --configKeyValues +/// +/// \author Evgeny Kryshen + #ifndef O2_ECAL_BASEPARAM_H #define O2_ECAL_BASEPARAM_H -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" +#include +#include namespace o2 { namespace ecal { struct ECalBaseParam : public o2::conf::ConfigurableParamHelper { - float rMin = 125.0; // cm - float rMax = 155.0; // cm - float zLength = 350.0; // cm - - bool enableFwdEndcap = true; + bool enableFwdEndcap = false; + // general ecal barrel settings + double rMin = 125; // cm + double rMax = 155; // cm + double zLength = 350; // cm + int nSuperModules = 4; + // crystal module specification + int nCrystalModulesZ = 31; + int nCrystalModulesPhi = 96; + double crystalAlphaDeg = 0.4; // degrees + double crystalModuleWidth = 1.9; // cm + double crystalModuleLength = 18; // cm + // sampling module specification + int nSamplingModulesZ = 56; + int nSamplingModulesPhi = 67; + double samplingAlphaDeg = 0.4; // degrees + double samplingModuleWidth = 2.7; // cm + double frontPlateThickness = 1.; // cm + double pbLayerThickness = 0.12; // cm + double scLayerThickness = 0.15; // cm + int nSamplingLayers = 80; + // margin in z between crystal modules and sampling modules + double marginCrystalToSampling = 0.1; // cm O2ParamDef(ECalBaseParam, "ECalBase"); }; @@ -32,4 +55,4 @@ struct ECalBaseParam : public o2::conf::ConfigurableParamHelper { } // namespace ecal } // end namespace o2 -#endif \ No newline at end of file +#endif // O2_ECAL_BASEPARAM_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h new file mode 100644 index 0000000000000..a780e36f45938 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h @@ -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 Geometry.h +/// \brief Geometry helper class +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_GEOMETRY_H +#define ALICEO2_ECAL_GEOMETRY_H + +#include +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Geometry +{ + public: + static Geometry& instance() + { + static Geometry sGeom; + return sGeom; + } + + int getNcols() const; + int getNrows() const; + std::pair getSectorChamber(int cellId) const; + std::pair getSectorChamber(int iphi, int iz) const; + + void fillFrontFaceCenterCoordinates(); + int getCellID(int moduleId, int sectorId, bool isCrystal); + void detIdToRelIndex(int cellId, int& chamber, int& sector, int& iphi, int& iz) const; + void detIdToGlobalPosition(int detId, double& x, double& y, double& z); + std::pair globalRowColFromIndex(int cellID) const; + bool isCrystal(int cellID); + int areNeighboursVertex(int detId1, int detId2) const; + + double getTanBeta(int i) { return mTanBeta[i]; } + double getFrontFaceZatMinR(int i) { return mFrontFaceZatMinR[i]; } + double getFrontFaceCenterR(int i) { return mFrontFaceCenterR[i]; } + double getFrontFaceCenterZ(int i) { return mFrontFaceCenterZ[i]; } + double getFrontFaceCenterSamplingPhi(int i) { return mFrontFaceCenterSamplingPhi[i]; } + double getFrontFaceCenterCrystalPhi(int i) { return mFrontFaceCenterCrystalPhi[i]; } + double getFrontFaceCenterTheta(int i) { return mFrontFaceCenterTheta[i]; } + double getRMin() { return mRMin; } + double getCrystalModW() { return mCrystalModW; } + double getSamplingModW() { return mSamplingModW; } + double getCrystalAlpha() { return mCrystalAlpha; } + double getSamplingAlpha() { return mSamplingAlpha; } + double getCrystalDeltaPhi() { return 2 * std::atan(mCrystalModW / 2 / mRMin); } + double getSamplingDeltaPhi() { return 2 * std::atan(mSamplingModW / 2 / mRMin); } + double getFrontFaceMaxEta(int i); + double getCrystalPhiMin(); + double getSamplingPhiMin(); + int getNModulesZ() { return mNModulesZ; } + bool isAtTheEdge(int cellId); + + private: + Geometry(); + Geometry(const Geometry&) = delete; + Geometry& operator=(const Geometry&) = delete; + ~Geometry() = default; + double mRMin{0.}; + int mNSuperModules{0}; + int mNCrystalModulesZ{0}; + int mNSamplingModulesZ{0}; + int mNCrystalModulesPhi{0}; + int mNSamplingModulesPhi{0}; + double mCrystalModW{0.}; + double mSamplingModW{0.}; + double mSamplingAlpha{0.}; + double mCrystalAlpha{0.}; + double mMarginCrystalToSampling{0.}; + int mNModulesZ{0}; + std::vector mFrontFaceZatMinR; + std::vector mFrontFaceCenterR; + std::vector mFrontFaceCenterZ; + std::vector mFrontFaceCenterSamplingPhi; + std::vector mFrontFaceCenterCrystalPhi; + std::vector mFrontFaceCenterTheta; + std::vector mTanBeta; + + ClassDefNV(Geometry, 1); +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_GEOMETRY_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h index 1cff6dd7d3313..6975a5378a72f 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h @@ -9,6 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GeometryTGeo.h +/// \brief Class containing ECal volume naming patterns +/// +/// \author Evgeny Kryshen + #ifndef ALICEO2_ECAL_GEOMETRYTGEO_H #define ALICEO2_ECAL_GEOMETRYTGEO_H @@ -27,21 +32,25 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static GeometryTGeo* Instance(); static const char* getECalVolPattern() { return sVolumeName.c_str(); } - static const char* getECalSensorPattern() { return sSensorName.c_str(); } + static const char* getECalSectorPattern() { return sSectorName.c_str(); } + static const char* getECalModulePattern() { return sModuleName.c_str(); } static const char* composeSymNameECal() { return Form("%s_%d", o2::detectors::DetID(o2::detectors::DetID::ECL).getName(), 0); } - static const char* composeSymNameSensor(); // A single sensor for the moment + static const char* composeSymNameSector(int s); + static const char* composeSymNameModule(int s, int m); protected: static std::string sVolumeName; - static std::string sSensorName; + static std::string sSectorName; + static std::string sModuleName; private: static std::unique_ptr sInstance; }; } // namespace ecal } // namespace o2 -#endif + +#endif // ALICEO2_ECAL_GEOMETRYTGEO_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h new file mode 100644 index 0000000000000..006b2df5949e6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h @@ -0,0 +1,85 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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 MC hit class to store energy loss per cell and per superparent +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_HIT_H +#define ALICEO2_ECAL_HIT_H + +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Hit : public o2::BasicXYZEHit +{ + public: + /// \brief Default constructor + Hit() = default; + + /// \brief Hit constructor + /// + /// Fully defining information of the ECAL point (position, momentum, energy, track, ...) + /// + /// \param trackID Index of the track, defined as parent track entering the ECAL + /// \param cellID ID of the detector cell + /// \param pos Position vector of the point + /// \param mom Momentum vector for the particle at the point + /// \param tof Time of the hit + /// \param eLoss Energy loss + Hit(int trackID, int cellID, const math_utils::Point3D& pos, + const math_utils::Vector3D& mom, float tof, float eLoss) + : o2::BasicXYZEHit(pos.X(), pos.Y(), pos.Z(), tof, eLoss, trackID, 0), + mPvector(mom), + mCellID(cellID) + { + } + + /// \brief Destructor + ~Hit() = default; + + /// \brief Check whether the points are from the same parent and in the same detector volume + /// \return True if points are the same (origin and detector), false otherwise + bool operator==(const Hit& rhs) const; + + /// \brief Sorting points according to parent particle and detector volume + /// \return True if this point is smaller, false otherwise + bool operator<(const Hit& rhs) const; + + /// \brief Get cell ID + /// \return cell ID + int GetCellID() const { return mCellID; } + + private: + math_utils::Vector3D mPvector; ///< Momentum vector + int32_t mCellID; ///< Cell ID (used instead of short detID) + ClassDefNV(Hit, 1); +}; + +} // namespace ecal +} // namespace o2 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; +} // namespace std +#endif + +#endif // ALICEO2_ECAL_HIT_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h index 3bf7ccd32460c..0f0c0637ce2c1 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h @@ -15,8 +15,11 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::ecal::Geometry + ; #pragma link C++ class o2::ecal::GeometryTGeo + #pragma link C++ class o2::ecal::ECalBaseParam + ; +#pragma link C++ class o2::ecal::Hit + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::ecal::ECalBaseParam> + ; +#pragma link C++ class std::vector < o2::ecal::Hit> + ; #endif \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx index 54eb2860526b3..6847f42e26346 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx @@ -9,6 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ECalBase/ECalBaseParam.h" +/// \file ECalBaseParam.cxx +/// \brief Geometry parameters configurable via o2-sim --configKeyValues +/// +/// \author Evgeny Kryshen -O2ParamImpl(o2::ecal::ECalBaseParam); \ No newline at end of file +#include + +O2ParamImpl(o2::ecal::ECalBaseParam); diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx new file mode 100644 index 0000000000000..2d6bdf160f393 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx @@ -0,0 +1,278 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Geometry.cxx +/// \brief Geometry helper class +/// +/// \author Evgeny Kryshen + +#include "TMath.h" +#include +#include +#include +#include +#include "CommonConstants/MathConstants.h" +using namespace o2::ecal; +using o2::constants::math::PIHalf; +using o2::constants::math::TwoPI; + +//============================================================================== +Geometry::Geometry() +{ + auto& pars = ECalBaseParam::Instance(); + pars.updateFromFile("o2sim_configuration.ini", "ECalBase"); + pars.printKeyValues(false, true, true, false); + mRMin = pars.rMin; + mNSuperModules = pars.nSuperModules; + mNCrystalModulesZ = pars.nCrystalModulesZ; + mNSamplingModulesZ = pars.nSamplingModulesZ; + mNCrystalModulesPhi = pars.nCrystalModulesPhi; + mNSamplingModulesPhi = pars.nSamplingModulesPhi; + mCrystalModW = pars.crystalModuleWidth; + mSamplingModW = pars.samplingModuleWidth; + mMarginCrystalToSampling = pars.marginCrystalToSampling; + mCrystalAlpha = pars.crystalAlphaDeg * TMath::DegToRad(); + mSamplingAlpha = pars.samplingAlphaDeg * TMath::DegToRad(); + mNModulesZ = 2 * mNSamplingModulesZ + 2 * mNCrystalModulesZ; + fillFrontFaceCenterCoordinates(); +} + +//============================================================================== +int Geometry::getNcols() const +{ + return mNModulesZ; +} + +//============================================================================== +int Geometry::getNrows() const +{ + return mNSuperModules * (mNCrystalModulesPhi > mNSamplingModulesPhi ? mNCrystalModulesPhi : mNSamplingModulesPhi); +} + +//============================================================================== +double Geometry::getCrystalPhiMin() +{ + double superModuleDeltaPhi = TwoPI / mNSuperModules; + double crystalDeltaPhi = getCrystalDeltaPhi(); + return (superModuleDeltaPhi - crystalDeltaPhi * mNCrystalModulesPhi) / 2.; +} + +//============================================================================== +double Geometry::getSamplingPhiMin() +{ + double superModuleDeltaPhi = TwoPI / mNSuperModules; + double samplingDeltaPhi = getSamplingDeltaPhi(); + return (superModuleDeltaPhi - samplingDeltaPhi * mNSamplingModulesPhi) / 2.; +} + +double Geometry::getFrontFaceMaxEta(int i) +{ + double theta = std::atan(mRMin / getFrontFaceZatMinR(i)); + return -std::log(std::tan(theta / 2.)); +} + +//============================================================================== +void Geometry::fillFrontFaceCenterCoordinates() +{ + if (mFrontFaceCenterR.size() > 0) { + return; + } + mFrontFaceCenterTheta.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceZatMinR.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceCenterR.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceCenterZ.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mTanBeta.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceCenterSamplingPhi.resize(mNSuperModules * mNSamplingModulesPhi); + mFrontFaceCenterCrystalPhi.resize(mNSuperModules * mNCrystalModulesPhi); + + double superModuleDeltaPhi = TwoPI / mNSuperModules; + double crystalDeltaPhi = getCrystalDeltaPhi(); + double samplingDeltaPhi = getSamplingDeltaPhi(); + double crystalPhiMin = getCrystalPhiMin(); + double samplingPhiMin = getSamplingPhiMin(); + for (int ism = 0; ism < mNSuperModules; ism++) { + // crystal + for (int i = 0; i < mNCrystalModulesPhi; i++) { + double phi0 = superModuleDeltaPhi * ism + crystalPhiMin + crystalDeltaPhi * i; + mFrontFaceCenterCrystalPhi[ism * mNCrystalModulesPhi + i] = phi0; + } + // sampling + for (int i = 0; i < mNSamplingModulesPhi; i++) { + double phi0 = superModuleDeltaPhi * ism + samplingPhiMin + samplingDeltaPhi * i; + mFrontFaceCenterSamplingPhi[ism * mNSamplingModulesPhi + i] = phi0; + } + } + + double theta0 = PIHalf - mCrystalAlpha; + double zAtMinR = mCrystalModW * std::cos(mCrystalAlpha); + + for (int m = 0; m < mNCrystalModulesZ; m++) { + mTanBeta[m] = std::sin(theta0 - mCrystalAlpha) * mCrystalModW / 2 / mRMin; + ROOT::Math::Polar2DVector vMid21(mCrystalModW / 2., PIHalf + theta0); + ROOT::Math::XYPoint pAtMinR(zAtMinR, mRMin); + ROOT::Math::XYPoint pc = pAtMinR + vMid21; + mFrontFaceZatMinR[m] = zAtMinR; + mFrontFaceCenterZ[m] = pc.x(); + mFrontFaceCenterR[m] = pc.y(); + mFrontFaceCenterTheta[m] = theta0; + theta0 -= 2 * mCrystalAlpha; + zAtMinR += mCrystalModW * std::cos(mCrystalAlpha) / std::sin(theta0 + mCrystalAlpha); + } + + theta0 = mFrontFaceCenterTheta[mNCrystalModulesZ - 1] - mCrystalAlpha - mSamplingAlpha; + zAtMinR = mFrontFaceZatMinR[mNCrystalModulesZ - 1]; + zAtMinR += mSamplingModW * std::cos(mSamplingAlpha) / std::sin(theta0 + mSamplingAlpha); + zAtMinR += mMarginCrystalToSampling; + + for (int m = 0; m < mNSamplingModulesZ; m++) { + int i = m + mNCrystalModulesZ; + mTanBeta[i] = std::sin(theta0 - mSamplingAlpha) * mSamplingModW / 2 / mRMin; + ROOT::Math::Polar2DVector vMid21(mSamplingModW / 2., PIHalf + theta0); + ROOT::Math::XYPoint pAtMinR(zAtMinR, mRMin); + ROOT::Math::XYPoint pc = pAtMinR + vMid21; + mFrontFaceZatMinR[i] = zAtMinR; + mFrontFaceCenterZ[i] = pc.x(); + mFrontFaceCenterR[i] = pc.y(); + mFrontFaceCenterTheta[i] = theta0; + theta0 -= 2 * mSamplingAlpha; + zAtMinR += mSamplingModW * std::cos(mSamplingAlpha) / std::sin(theta0 + mSamplingAlpha); + } +} + +int Geometry::getCellID(int moduleId, int sectorId, bool isCrystal) +{ + int cellID = 0; + if (isCrystal) { + if (moduleId % 2 == 0) { // crystal at positive eta + cellID = sectorId * mNModulesZ + moduleId / 2 + mNSamplingModulesZ + mNCrystalModulesZ; + } else { // crystal at negative eta + cellID = sectorId * mNModulesZ - moduleId / 2 + mNSamplingModulesZ + mNCrystalModulesZ - 1; + } + } else { + if (sectorId % 2 == 0) { // sampling at positive eta + cellID = sectorId / 2 * mNModulesZ + moduleId + mNSamplingModulesZ + mNCrystalModulesZ * 2; + } else { // sampling at negative eta + cellID = sectorId / 2 * mNModulesZ - moduleId + mNSamplingModulesZ - 1; + } + } + return cellID; +} + +//============================================================================== +std::pair Geometry::globalRowColFromIndex(int cellID) const +{ + int ip = cellID / mNModulesZ; // row + int iz = cellID % mNModulesZ; // col + return {ip, iz}; +} + +//============================================================================== +bool Geometry::isCrystal(int cellID) +{ + auto [row, col] = globalRowColFromIndex(cellID); + return (col >= mNSamplingModulesZ && col < mNSamplingModulesZ + 2 * mNCrystalModulesZ); +} + +//============================================================================== +std::pair Geometry::getSectorChamber(int cellId) const +{ + int iphi = cellId / mNModulesZ; + int iz = cellId % mNModulesZ; + return getSectorChamber(iphi, iz); +} + +//============================================================================== +std::pair Geometry::getSectorChamber(int iphi, int iz) const +{ + int chamber = iz < mNSamplingModulesZ ? 0 : (iz < mNSamplingModulesZ + 2 * mNCrystalModulesZ ? 1 : 2); + int sector = iphi / (chamber == 1 ? mNCrystalModulesPhi : mNSamplingModulesPhi); + return {sector, chamber}; +} + +//============================================================================== +void Geometry::detIdToRelIndex(int cellId, int& chamber, int& sector, int& iphi, int& iz) const +{ + // 3 chambers - sampling/crystal/sampling + iphi = cellId / mNModulesZ; + iz = cellId % mNModulesZ; + auto pair = getSectorChamber(iphi, iz); + sector = pair.first; + chamber = pair.second; +} + +//============================================================================== +void Geometry::detIdToGlobalPosition(int detId, double& x, double& y, double& z) +{ + int chamber, sector, iphi, iz; + detIdToRelIndex(detId, chamber, sector, iphi, iz); + double r = 0; + if (iz < mNSamplingModulesZ + mNCrystalModulesZ) { + z = -mFrontFaceCenterZ[mNSamplingModulesZ + mNCrystalModulesZ - iz - 1]; + r = mFrontFaceCenterR[mNSamplingModulesZ + mNCrystalModulesZ - iz - 1]; + } else { + z = mFrontFaceCenterZ[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; + r = mFrontFaceCenterR[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; + } + double phi = chamber == 1 ? mFrontFaceCenterCrystalPhi[iphi] : mFrontFaceCenterSamplingPhi[iphi]; + x = r * std::cos(phi); + y = r * std::sin(phi); +} + +//============================================================================== +int Geometry::areNeighboursVertex(int detId1, int detId2) const +{ + int ch1, sector1, iphi1, iz1; + int ch2, sector2, iphi2, iz2; + detIdToRelIndex(detId1, ch1, sector1, iphi1, iz1); + detIdToRelIndex(detId2, ch2, sector2, iphi2, iz2); + if (sector1 != sector2 || ch1 != ch2) { + return 0; + } + if (std::abs(iphi1 - iphi2) <= 1 && std::abs(iz1 - iz2) <= 1) { + return 1; + } + return 0; +} + +//============================================================================== +bool Geometry::isAtTheEdge(int cellId) +{ + auto [row, col] = globalRowColFromIndex(cellId); + if (col == 0) { + return 1; + } else if (col == mNSamplingModulesZ) { + return 1; + } else if (col == mNSamplingModulesZ - 1) { + return 1; + } else if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ) { + return 1; + } else if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ - 1) { + return 1; + } else if (col == mNModulesZ - 1) { + return 1; + } + for (int m = 0; m <= mNSuperModules; m++) { + if (isCrystal(cellId)) { + if (row == m * mNCrystalModulesPhi) { + return 1; + } else if (row == m * mNCrystalModulesPhi - 1) { + return 1; + } + } else { + if (row == m * mNSamplingModulesPhi) { + return 1; + } else if (row == m * mNSamplingModulesPhi - 1) { + return 1; + } + } + } + return 0; +} diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx index 49f57d8a8c5cc..aca4f5548dc51 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx @@ -9,6 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GeometryTGeo.cxx +/// \brief Class containing ECal volume naming patterns +/// +/// \author Evgeny Kryshen + #include #include @@ -19,7 +24,8 @@ namespace ecal std::unique_ptr GeometryTGeo::sInstance; std::string GeometryTGeo::sVolumeName = "ECALV"; -std::string GeometryTGeo::sSensorName = "ECALSensor"; +std::string GeometryTGeo::sSectorName = "ECALSector"; +std::string GeometryTGeo::sModuleName = "ECALModule"; GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : DetMatrixCache() { @@ -57,10 +63,15 @@ GeometryTGeo* GeometryTGeo::Instance() return sInstance.get(); } -const char* GeometryTGeo::composeSymNameSensor() +const char* GeometryTGeo::composeSymNameSector(int s) +{ + return Form("%s/%s_%d", composeSymNameECal(), getECalSectorPattern(), s); +} + +const char* GeometryTGeo::composeSymNameModule(int s, int m) { - return Form("%s/%d", composeSymNameECal(), 0); + return Form("%s/%s_%d", composeSymNameSector(s), getECalModulePattern(), m); } } // namespace ecal -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx new file mode 100644 index 0000000000000..ee2034314d2d8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.cxx +/// \brief MC hit class to store energy loss per cell and per superparent +/// +/// \author Evgeny Kryshen + +#include + +ClassImp(o2::ecal::Hit); + +using namespace o2::ecal; + +bool Hit::operator<(const Hit& rhs) const +{ + if (GetTrackID() != rhs.GetTrackID()) { + return GetTrackID() < rhs.GetTrackID(); + } + return GetCellID() < rhs.GetCellID(); +} + +bool Hit::operator==(const Hit& rhs) const +{ + return (GetCellID() == rhs.GetCellID()) && (GetTrackID() == rhs.GetTrackID()); +} diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..f51a9c067d6b3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ECalReconstruction + SOURCES src/Clusterizer.cxx + PUBLIC_LINK_LIBRARIES O2::ECalBase + O2::DataFormatsECal + AliceO2::InfoLogger) + +o2_target_root_dictionary(ECalReconstruction + HEADERS include/ECalReconstruction/Clusterizer.h) diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h b/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h new file mode 100644 index 0000000000000..5e4d36f831360 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Clusterizer.h +/// \brief Class for cluster finding and unfolding +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_CLUSTERIZER_H +#define ALICEO2_ECAL_CLUSTERIZER_H + +#include +#include +#include +#include + +using o2::ecal::Cluster; +using o2::ecal::Digit; +class TF1; + +namespace o2 +{ +namespace ecal +{ +class Clusterizer +{ + public: + Clusterizer(bool applyCorrectionZ = 1, bool applyCorrectionE = 1); + ~Clusterizer() = default; + void initialize() {}; + void addDigitToCluster(Cluster& cluster, int row, int col, const gsl::span& digits); + void findClusters(const gsl::span& digits, std::vector& foundClusters, std::vector& unfoldedClusters); + void makeClusters(const gsl::span& digits, std::vector& clusters); + void makeUnfoldings(std::vector& foundClusters, std::vector& unfoldedClusters); + void unfoldOneCluster(Cluster* iniClu, int nMax, int* digitId, float* maxAtEnergy, std::vector& unfoldedClusters); + void evalClusters(std::vector& clusters); + int getNumberOfLocalMax(Cluster& clu, int* maxAt, float* maxAtEnergy); + double showerShape(double dx, double dz, bool isCrystal); + void setLogWeight(double logWeight) { mLogWeight = logWeight; } + void setClusteringThreshold(double threshold) { mClusteringThreshold = threshold; } + void setCrystalDigitThreshold(double threshold) { mCrystalDigitThreshold = threshold; } + void setSamplingDigitThreshold(double threshold) { mSamplingDigitThreshold = threshold; } + void setCrystalEnergyCorrectionPars(std::vector pars) { mCrystalEnergyCorrectionPars = pars; } + void setSamplingEnergyCorrectionPars(std::vector pars) { mSamplingEnergyCorrectionPars = pars; } + void setCrystalZCorrectionPars(std::vector pars) { mCrystalZCorrectionPars = pars; } + void setSamplingZCorrectionPars(std::vector pars) { mSamplingZCorrectionPars = pars; } + + private: + std::vector> mDigitIndices; // 2D map of digit indices used for recursive cluster finding + bool mUnfoldClusters = true; // to perform cluster unfolding + double mCrystalDigitThreshold = 0.040; // minimal energy of crystal digit + double mSamplingDigitThreshold = 0.100; // minimal energy of sampling digit + double mClusteringThreshold = 0.050; // minimal energy of digit to start clustering (GeV) + double mClusteringTimeGate = 1e9; // maximal time difference between digits to be accepted to clusters (in ns) + int mNLMMax = 30; // maximal number of local maxima in unfolding + double mLogWeight = 4.; // cutoff used in log. weight calculation + double mUnfogingEAccuracy = 1.e-4; // accuracy of energy calculation in unfoding prosedure (GeV) + double mUnfogingXZAccuracy = 1.e-2; // accuracy of position calculation in unfolding procedure (cm) + int mNMaxIterations = 100; // maximal number of iterations in unfolding procedure + double mLocalMaximumCut = 0.015; // minimal height of local maximum over neighbours + bool mApplyCorrectionZ = 1; // apply z-correction + bool mApplyCorrectionE = 1; // apply energy-correction + TF1* fCrystalShowerShape; //! Crystal shower shape + TF1* fSamplingShowerShape; //! Sampling shower shape + TF1* fCrystalRMS; //! Crystal RMS + std::vector mCrystalEnergyCorrectionPars; // crystal energy-correction parameters + std::vector mSamplingEnergyCorrectionPars; // sampling energy-correction parameters + std::vector mCrystalZCorrectionPars; // crystal z-correction parameters + std::vector mSamplingZCorrectionPars; // sampling z-correction parameters +}; + +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_CLUSTERIZER_H diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx new file mode 100644 index 0000000000000..28efa78059dc1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx @@ -0,0 +1,494 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Clusterizer.cxx +/// \brief Class for cluster finding and unfolding +/// +/// \author Evgeny Kryshen + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::ecal; + +//============================================================================== +Clusterizer::Clusterizer(bool applyCorrectionZ, bool applyCorrectionE) +{ + auto& geo = Geometry::instance(); + mDigitIndices.resize(geo.getNrows(), std::vector(geo.getNcols(), -1)); + mApplyCorrectionZ = applyCorrectionZ; + mApplyCorrectionE = applyCorrectionE; + + mCrystalEnergyCorrectionPars.reserve(6); + mCrystalEnergyCorrectionPars[0] = 0.00444; + mCrystalEnergyCorrectionPars[1] = -1.322; + mCrystalEnergyCorrectionPars[2] = 1.021; + mCrystalEnergyCorrectionPars[3] = 0.0018; + mCrystalEnergyCorrectionPars[4] = 0.; + mCrystalEnergyCorrectionPars[5] = 0.; + + mSamplingEnergyCorrectionPars.reserve(6); + mSamplingEnergyCorrectionPars[0] = 0.0033; + mSamplingEnergyCorrectionPars[1] = -2.09; + mSamplingEnergyCorrectionPars[2] = 1.007; + mSamplingEnergyCorrectionPars[3] = 0.0667; + mSamplingEnergyCorrectionPars[4] = -0.108; + mSamplingEnergyCorrectionPars[5] = 0.0566; + + mCrystalZCorrectionPars.reserve(9); + mCrystalZCorrectionPars[0] = -0.005187; + mCrystalZCorrectionPars[1] = 0.7301; + mCrystalZCorrectionPars[2] = -0.7382; + mCrystalZCorrectionPars[3] = 0.; + mCrystalZCorrectionPars[4] = 0.; + mCrystalZCorrectionPars[5] = 0.; + mCrystalZCorrectionPars[6] = 0.; + mCrystalZCorrectionPars[7] = 0.; + mCrystalZCorrectionPars[8] = 0.; + + mSamplingZCorrectionPars.reserve(9); + mSamplingZCorrectionPars[0] = -2.137; + mSamplingZCorrectionPars[1] = 6.400; + mSamplingZCorrectionPars[2] = -3.342; + mSamplingZCorrectionPars[3] = -0.1364; + mSamplingZCorrectionPars[4] = 0.4019; + mSamplingZCorrectionPars[5] = -0.1969; + mSamplingZCorrectionPars[6] = 0.008223; + mSamplingZCorrectionPars[7] = -0.02425; + mSamplingZCorrectionPars[8] = 0.01190; + + fCrystalShowerShape = new TF1("fCrystal", "x<[1] ? [0]*exp([3]*x+[4]*x*x+[5]*x*x*x) : (x<[2] ? [0]*[6]*exp([7]*x+[8]*x*x) : [0]*[9]*exp([10]*x+[11]*x*x))", 0, 15); + double pc[12]; + pc[0] = 1. / 13.15; + pc[1] = 2.2; + pc[2] = 5; + pc[3] = 4.38969; + pc[4] = -5.15975; + pc[5] = 1.18978; + pc[6] = 1.48726; + pc[7] = -1.54621; + pc[8] = 0.0814617; + pc[9] = 0.0369055; + pc[10] = -0.174372; + pc[11] = -0.0455978; + + fCrystalShowerShape->SetParameters(pc); + + fSamplingShowerShape = new TF1("fSampling", "x<[1] ? [0]*exp([3]*x+[4]*x*x+[5]*x*x*x) : (x<[2] ? [0]*[6]*exp([7]*x+[8]*x*x) : [0]*[9]*exp([10]*x+[11]*x*x))", 0, 15); + double ps[12]; + ps[0] = 1 / 35.6; + ps[1] = 3.2; + ps[2] = 6; + ps[3] = 3.06543; + ps[4] = -2.23235; + ps[5] = 0.325344; + ps[6] = 6.0733; + ps[7] = -1.62713; + ps[8] = 0.0965569; + ps[9] = 0.0765706; + ps[10] = -0.217398; + ps[11] = -0.0204646; + fSamplingShowerShape->SetParameters(ps); + + fCrystalRMS = new TF1("fCrystalRMS", "[0]*x*exp([1]*x+[2]*x*x+[3]*x*x*x)", 0, 2.2); + double p[4]; + p[0] = 1.39814; + p[1] = -6.05426; + p[2] = 6.26678; + p[3] = -1.97092; + fCrystalRMS->SetParameters(p); +} + +//============================================================================== +void Clusterizer::findClusters(const gsl::span& digits, std::vector& foundClusters, std::vector& unfoldedClusters) +{ + foundClusters.clear(); + unfoldedClusters.clear(); + + // Collect list of clusters + makeClusters(digits, foundClusters); + + // Split clusters with several local maxima if necessary + makeUnfoldings(foundClusters, unfoldedClusters); + + // Evaluate cluster position, dispersion etc. + evalClusters(foundClusters); + evalClusters(unfoldedClusters); +} + +//============================================================================== +void Clusterizer::addDigitToCluster(Cluster& cluster, int row, int col, const gsl::span& digits) +{ + auto& geo = Geometry::instance(); + if (row < 0 || row >= geo.getNrows() || col < 0 || col >= geo.getNcols()) { + return; + } + int digitIndex = mDigitIndices[row][col]; + LOGP(debug, " checking row={} and col={} digitIndex={}", row, col, digitIndex); + if (digitIndex < 0) { + return; + } + const Digit& digit = digits[digitIndex]; + if (cluster.getMultiplicity() > 0) { + // check if new digit is in the same chamber and sector + const Digit& digit2 = digits[cluster.getDigitIndex(0)]; + auto [sector1, ch1] = geo.getSectorChamber(digit.getTower()); + auto [sector2, ch2] = geo.getSectorChamber(digit2.getTower()); + LOGP(debug, " checking if sector and chamber are the same: ({},{}) ({},{})", sector1, ch1, sector2, ch2); + if (sector1 != sector2 || ch1 != ch2) { + return; + } + } + + mDigitIndices[row][col] = -1; + cluster.addDigit(digitIndex, digit.getTower(), digit.getEnergy()); + LOGP(debug, " adding new digit at row={} and col={}", row, col); + addDigitToCluster(cluster, row - 1, col, digits); + addDigitToCluster(cluster, row + 1, col, digits); + addDigitToCluster(cluster, row, col - 1, digits); + addDigitToCluster(cluster, row, col + 1, digits); +} + +//============================================================================== +void Clusterizer::makeClusters(const gsl::span& digits, std::vector& clusters) +{ + // Combine digits into cluster + + int nDigits = digits.size(); + + // reset mDigitIndices + for (auto& rows : mDigitIndices) { + rows.assign(rows.size(), -1); + } + + // fill mDigitIndices + auto& geo = Geometry::instance(); + for (int i = 0; i < nDigits; i++) { + const Digit& digit = digits[i]; + auto [row, col] = geo.globalRowColFromIndex(digit.getTower()); + bool isCrystal = geo.isCrystal(digit.getTower()); + if (isCrystal) { + if (digit.getEnergy() < mCrystalDigitThreshold) { + continue; + } + } else { + if (digit.getEnergy() < mSamplingDigitThreshold) { + continue; + } + } + mDigitIndices[row][col] = i; + } + + // add digit seeds to clusters and recursively add neighbours + for (int i = 0; i < nDigits; i++) { + const Digit& digitSeed = digits[i]; + auto [row, col] = geo.globalRowColFromIndex(digitSeed.getTower()); + if (mDigitIndices[row][col] < 0) { + continue; // digit was already added in one of the clusters + } + if (digitSeed.getEnergy() < mClusteringThreshold) { + continue; + } + LOGP(debug, " starting new cluster at row={} and col={}", row, col); + auto& cluster = clusters.emplace_back(); + addDigitToCluster(cluster, row, col, digits); + } + + LOGP(debug, "made {} clusters from {} digits", clusters.size(), nDigits); +} + +//============================================================================== +void Clusterizer::makeUnfoldings(std::vector& foundClusters, std::vector& unfoldedClusters) +{ + // Split cluster if several local maxima are found + if (!mUnfoldClusters) { + return; + } + + int* maxAt = new int[mNLMMax]; + float* maxAtEnergy = new float[mNLMMax]; + + for (auto& clu : foundClusters) { + int nMax = getNumberOfLocalMax(clu, maxAt, maxAtEnergy); + if (nMax > 1) { + unfoldOneCluster(&clu, nMax, maxAt, maxAtEnergy, unfoldedClusters); + } else { + clu.setNLM(1); + unfoldedClusters.emplace_back(clu); + } + } + delete[] maxAt; + delete[] maxAtEnergy; +} + +//============================================================================== +void Clusterizer::unfoldOneCluster(Cluster* iniClu, int nMax, int* digitId, float* maxAtEnergy, std::vector& unfoldedClusters) +{ + // Based on MpdEmcClusterizerKI::UnfoldOneCluster by D. Peresunko + // Performs the unfolding of a cluster with nMax overlapping showers + // Parameters: iniClu cluster to be unfolded + // nMax number of local maxima found (this is the number of new clusters) + // digitId: index of digits, corresponding to local maxima + // maxAtEnergy: energies of digits, corresponding to local maxima + + // Take initial cluster and calculate local coordinates of digits + // To avoid multiple re-calculation of same parameters + int mult = iniClu->getMultiplicity(); + std::vector x(mult); + std::vector y(mult); + std::vector z(mult); + std::vector e(mult); + std::vector> eInClusters(mult, std::vector(nMax)); + + auto& geo = Geometry::instance(); + bool isCrystal = geo.isCrystal(iniClu->getDigitTowerId(0)); + + for (int idig = 0; idig < mult; idig++) { + e[idig] = iniClu->getDigitEnergy(idig); + geo.detIdToGlobalPosition(iniClu->getDigitTowerId(idig), x[idig], y[idig], z[idig]); + } + + // Coordinates of centers of clusters + std::vector xMax(nMax); + std::vector yMax(nMax); + std::vector zMax(nMax); + std::vector eMax(nMax); + + for (int iclu = 0; iclu < nMax; iclu++) { + xMax[iclu] = x[digitId[iclu]]; + yMax[iclu] = y[digitId[iclu]]; + zMax[iclu] = z[digitId[iclu]]; + eMax[iclu] = e[digitId[iclu]]; + } + + std::vector prop(nMax); // proportion of clusters in the current digit + + // Try to decompose cluster to contributions + int nIterations = 0; + bool insuficientAccuracy = true; + + while (insuficientAccuracy && nIterations < mNMaxIterations) { + // Loop over all digits of parent cluster and split their energies between daughter clusters + // according to shower shape + for (int idig = 0; idig < mult; idig++) { + double eEstimated = 0; + for (int iclu = 0; iclu < nMax; iclu++) { + prop[iclu] = eMax[iclu] * showerShape(std::sqrt((x[idig] - xMax[iclu]) * (x[idig] - xMax[iclu]) + + (y[idig] - yMax[iclu]) * (y[idig] - yMax[iclu])), + z[idig] - zMax[iclu], isCrystal); + eEstimated += prop[iclu]; + } + if (eEstimated == 0.) { // numerical accuracy + continue; + } + // Split energy of digit according to contributions + for (int iclu = 0; iclu < nMax; iclu++) { + eInClusters[idig][iclu] = e[idig] * prop[iclu] / eEstimated; + } + } + // Recalculate parameters of clusters and check relative variation of energy and absolute of position + insuficientAccuracy = false; // will be true if at least one parameter changed too much + for (int iclu = 0; iclu < nMax; iclu++) { + double oldX = xMax[iclu]; + double oldY = yMax[iclu]; + double oldZ = zMax[iclu]; + double oldE = eMax[iclu]; + // new energy, need for weight + eMax[iclu] = 0; + for (int idig = 0; idig < mult; idig++) { + eMax[iclu] += eInClusters[idig][iclu]; + } + xMax[iclu] = 0; + yMax[iclu] = 0; + zMax[iclu] = 0; + double wtot = 0.; + for (int idig = 0; idig < mult; idig++) { + double w = std::max(std::log(eInClusters[idig][iclu] / eMax[iclu]) + mLogWeight, 0.); + xMax[iclu] += x[idig] * w; + yMax[iclu] += y[idig] * w; + zMax[iclu] += z[idig] * w; + wtot += w; + } + if (wtot > 0.) { + xMax[iclu] /= wtot; + yMax[iclu] /= wtot; + zMax[iclu] /= wtot; + } + // Compare variation of parameters + insuficientAccuracy += (std::abs(eMax[iclu] - oldE) > mUnfogingEAccuracy); + insuficientAccuracy += (std::abs(xMax[iclu] - oldX) > mUnfogingXZAccuracy); + insuficientAccuracy += (std::abs(yMax[iclu] - oldY) > mUnfogingXZAccuracy); + insuficientAccuracy += (std::abs(zMax[iclu] - oldZ) > mUnfogingXZAccuracy); + } + nIterations++; + } + + // Iterations finished, add new clusters + for (int iclu = 0; iclu < nMax; iclu++) { + auto& clu = unfoldedClusters.emplace_back(); + clu.setNLM(nMax); + for (int idig = 0; idig < mult; idig++) { + int jdigit = iniClu->getDigitIndex(idig); + int towerId = iniClu->getDigitTowerId(idig); + clu.addDigit(jdigit, towerId, eInClusters[idig][iclu]); + } + } +} + +//============================================================================== +void Clusterizer::evalClusters(std::vector& clusters) +{ + auto& geo = Geometry::instance(); + for (auto& cluster : clusters) { + double x = 0; + double y = 0; + double z = 0; + double wtot = 0; + double etot = cluster.getE(); + for (size_t i = 0; i < cluster.getMultiplicity(); i++) { + float energy = cluster.getDigitEnergy(i); + int towerId = cluster.getDigitTowerId(i); + double xi, yi, zi; + geo.detIdToGlobalPosition(towerId, xi, yi, zi); + double w = std::max(0., mLogWeight + std::log(energy / etot)); + x += w * xi; + y += w * yi; + z += w * zi; + wtot += w; + } + if (wtot != 0) { + x /= wtot; + y /= wtot; + z /= wtot; + } + cluster.setX(x); + cluster.setY(y); + cluster.setZ(z); + + // cluster shape + float chi2 = 0; + int ndf = 0; + float ee = cluster.getE(); + for (size_t i = 0; i < cluster.getMultiplicity(); i++) { + float energy = cluster.getDigitEnergy(i); + int towerId = cluster.getDigitTowerId(i); + double xi, yi, zi; + geo.detIdToGlobalPosition(towerId, xi, yi, zi); + double r = std::sqrt((x - xi) * (x - xi) + (y - yi) * (y - yi) + (z - zi) * (z - zi)); + if (r > 2.2) { + continue; + } + double frac = fCrystalShowerShape->Eval(r); + double rms = fCrystalRMS->Eval(r); + chi2 += std::pow((energy / ee - frac) / rms, 2.); + ndf++; + } + cluster.setChi2(chi2 / ndf); + + // correct cluster energy and z position + float eta = std::abs(cluster.getEta()); + bool isCrystal = geo.isCrystal(cluster.getDigitTowerId(0)); + if (mApplyCorrectionE) { + std::vector& pe = isCrystal ? mCrystalEnergyCorrectionPars : mSamplingEnergyCorrectionPars; + ee *= pe[0] * std::pow(ee, pe[1]) + pe[2] + pe[3] * eta + pe[4] * eta * eta + pe[5] * eta * eta * eta; + cluster.setE(ee); + } + if (mApplyCorrectionZ) { + std::vector& pz = isCrystal ? mCrystalZCorrectionPars : mSamplingZCorrectionPars; + float zCor = (pz[0] + pz[1] * eta + pz[2] * eta * eta) + (pz[3] + pz[4] * eta + pz[5] * eta * eta) * ee + (pz[6] + pz[7] * eta + pz[8] * eta * eta) * ee * ee; + cluster.setZ(z > 0 ? z - zCor : z + zCor); + } + + // check if cluster is at the edge of detector module + bool isEdge = 0; + for (size_t i = 0; i < cluster.getMultiplicity(); i++) { + int towerId = cluster.getDigitTowerId(i); + if (geo.isAtTheEdge(towerId)) { + isEdge = 1; + break; + } + } + cluster.setEdgeFlag(isEdge); + + LOGF(debug, "Cluster coordinates: (%6.2f,%6.2f,%6.2f)", cluster.getX(), cluster.getY(), cluster.getZ()); + } +} + +//============================================================================== +int Clusterizer::getNumberOfLocalMax(Cluster& clu, int* maxAt, float* maxAtEnergy) +{ + // Based on MpdEmcClusterizerKI::GetNumberOfLocalMax by D. Peresunko + // Calculates the number of local maxima in the cluster using LocalMaxCut as the minimum + // energy difference between maximum and surrounding digits + auto& geo = Geometry::instance(); + int n = clu.getMultiplicity(); + bool isCrystal = geo.isCrystal(clu.getDigitTowerId(0)); + bool* isLocalMax = new bool[n]; + + for (int i = 0; i < n; i++) { + isLocalMax[i] = false; + float en1 = clu.getDigitEnergy(i); + if (en1 > mClusteringThreshold) { + isLocalMax[i] = true; + } + } + + for (int i = 0; i < n; i++) { + int detId1 = clu.getDigitTowerId(i); + float en1 = clu.getDigitEnergy(i); + for (int j = i + 1; j < n; j++) { + int detId2 = clu.getDigitTowerId(j); + float en2 = clu.getDigitEnergy(j); + if (geo.areNeighboursVertex(detId1, detId2) == 1) { + if (en1 > en2) { + isLocalMax[j] = false; + // but may be digit too is not local max ? + if (en2 > en1 - mLocalMaximumCut) { + isLocalMax[i] = false; + } + } else { + isLocalMax[i] = false; + // but may be digitN is not local max too? + if (en1 > en2 - mLocalMaximumCut) { + isLocalMax[j] = false; + } + } + } // if neighbours + } // digit j + } // digit i + + int iDigitN = 0; + for (int i = 0; i < n; i++) { + if (isLocalMax[i]) { + maxAt[iDigitN] = i; + maxAtEnergy[iDigitN] = clu.getDigitEnergy(i); + iDigitN++; + if (iDigitN >= mNLMMax) { // Note that size of output arrays is limited: + LOGP(error, "Too many local maxima, cluster multiplicity {} region={}", n, isCrystal ? "crystal" : "sampling"); + return 0; + } + } + } + delete[] isLocalMax; + return iDigitN; +} + +//============================================================================== +double Clusterizer::showerShape(double dx, double dz, bool isCrystal) +{ + double x = std::sqrt(dx * dx + dz * dz); + return isCrystal ? fCrystalShowerShape->Eval(x) : fSamplingShowerShape->Eval(x); +} diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h new file mode 100644 index 0000000000000..d69cd8164e717 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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::ecal::Clusterizer + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt index 8c8c5a6bba15f..83de48e38db3a 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt @@ -11,8 +11,13 @@ o2_add_library(ECalSimulation SOURCES src/Detector.cxx + src/Digitizer.cxx PUBLIC_LINK_LIBRARIES O2::ECalBase - O2::ITSMFTSimulation) + O2::DataFormatsECal) o2_target_root_dictionary(ECalSimulation - HEADERS include/ECalSimulation/Detector.h) \ No newline at end of file + HEADERS include/ECalSimulation/Detector.h + include/ECalSimulation/Digitizer.h + ) + +o2_data_file(COPY data DESTINATION Detectors/ECL/simulation) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat b/Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat new file mode 100644 index 0000000000000..81aa69990f222 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat @@ -0,0 +1,14 @@ +* ECAL +* ==== +* +* Med GAM ELEC NHAD CHAD MUON EBREM MUHAB EDEL MUDEL MUPA ANNI BREM COMP DCAY DRAY HADR LOSS MULS PAIR PHOT RAYL STRA +* Air +ECL 0 5.e-5 1.e-4 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 +* Lead +ECL 1 1.e-4 1.e-4 1.e-3 1.e-3 1.e-3 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 +* Scintillator +ECL 2 1.e-4 1.e-4 1.e-3 1.e-3 1.e-3 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 +ECL 3 1.e-4 1.e-4 1.e-3 1.e-3 1.e-3 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 +* PWO +ECL 4 5.e-5 1.e-4 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 diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h index 14664092a8718..849dd69f85f2b 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h @@ -8,18 +8,18 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// Design and equations: Nicola Nicassio nicola.nicassio@cern.ch -// + +/// \file Detector.h +/// \brief ECal geometry creation and hit processing +/// +/// \author Evgeny Kryshen #ifndef ALICEO2_ECAL_DETECTOR_H #define ALICEO2_ECAL_DETECTOR_H -#include "DetectorsBase/Detector.h" -#include "ITSMFTSimulation/Hit.h" - -#include "ECalBase/GeometryTGeo.h" - +#include +#include +#include #include #include @@ -27,62 +27,35 @@ namespace o2 { namespace ecal { - class Detector : public o2::base::DetImpl { public: - Detector(bool active); - Detector(); + Detector(bool active = 1); ~Detector(); - void ConstructGeometry() override; - - o2::itsmft::Hit* addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, - const TVector3& startMom, double startE, double endTime, double eLoss, - unsigned char startStatus, unsigned char endStatus); - // Mandatory overrides - void BeginPrimary() override { ; } - void FinishPrimary() override { ; } + void ConstructGeometry() override; + void BeginPrimary() override {} + void FinishPrimary() override {} void InitializeO2Detector() override; - void PostTrack() override { ; } - void PreTrack() override { ; } + void PostTrack() override {} + void PreTrack() override {} bool ProcessHits(FairVolume* v = nullptr) override; - void EndOfEvent() override; + void EndOfEvent() override { Reset(); } void Register() override; void Reset() override; + std::vector* getHits(int iColl) const { return !iColl ? mHits : nullptr; } - // Custom memer functions - std::vector* getHits(int iColl) const - { - if (!iColl) { - return mHits; - } - return nullptr; - } - + private: void createMaterials(); void createGeometry(); - - private: - // Transient data about track passing the sensor - struct TrackData { - bool mHitStarted; // hit creation started - unsigned char mTrkStatusStart; // track status flag - TLorentzVector mPositionStart; // position at entrance - TLorentzVector mMomentumStart; // momentum - double mEnergyLoss; // energy loss - } mTrackData; //! transient data - - GeometryTGeo* mGeometryTGeo; //! - std::vector* mHits; // ITSMFT ones for the moment - - void defineSensitiveVolumes(); - float mInnerRadius; - float mOuterRadius; - float mLength; - - bool mEnableEndcap{true}; + void defineSamplingFactor(); + std::unordered_map mSuperParentIndices; //! Super parent indices (track index - superparent index) + int currentTrackId = -1; // current track index + int superparentId = -1; // superparent index + GeometryTGeo* mGeometryTGeo; //! + std::vector* mHits; //! + double mSamplingFactorTransportModel = 1.; protected: template @@ -104,4 +77,5 @@ struct UseShm { } // namespace base } // namespace o2 #endif -#endif \ No newline at end of file + +#endif // ALICEO2_ECAL_DETECTOR_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h new file mode 100644 index 0000000000000..91213fa90b63a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Digitizer.h +/// \brief Digitization of ECal MC information +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_DIGITIZER_H +#define ALICEO2_ECAL_DIGITIZER_H +#include +#include +#include +#include +#include + +using o2::ecal::Digit; +using o2::ecal::Hit; +using o2::ecal::MCLabel; + +namespace o2 +{ +namespace ecal +{ +class Digitizer +{ + public: + Digitizer(); + ~Digitizer() = default; + Digitizer(const Digitizer&) = delete; + Digitizer& operator=(const Digitizer&) = delete; + void init() {} + void finish() {} + void processHits(const std::vector* mHits, std::vector& digits, o2::dataformats::MCTruthContainer& labels, int collId); + void setThreshold(double threshold) { mThreshold = threshold; } + void setSmearCrystal(bool smearCrystal) { mSmearCrystal = smearCrystal; } + void setSamplingFraction(double fraction) { mSamplingFraction = fraction; } + void setCrystalPePerGeV(double pePerGeV) { mCrystalPePerGeV = pePerGeV; } + + private: + std::vector mArrayD; + bool mSmearCrystal = 0; + double mThreshold = 0.001; + double mSamplingFraction = 9.8; + double mCrystalPePerGeV = 4000; +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_DIGITIZER_H diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx index aeb58649fa4c5..f0de8aa4022a6 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx @@ -9,45 +9,40 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include +/// \file Detector.cxx +/// \brief ECal geometry creation and hit processing +/// +/// \author Evgeny Kryshen +#include #include #include +#include #include #include -#include - -#include "DetectorsBase/Stack.h" -#include "ITSMFTSimulation/Hit.h" -#include "ECalSimulation/Detector.h" -#include "ECalBase/ECalBaseParam.h" - -using o2::itsmft::Hit; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using o2::ecal::Hit; namespace o2 { namespace ecal { - -Detector::Detector() - : o2::base::DetImpl("ECL", true), - mTrackData(), - mHits(o2::utils::createSimVector()) -{ -} - Detector::Detector(bool active) - : o2::base::DetImpl("ECL", true), - mTrackData(), - mHits(o2::utils::createSimVector()) + : o2::base::DetImpl("ECL", active), + mHits(o2::utils::createSimVector()) { - auto& ecalPars = ECalBaseParam::Instance(); - mInnerRadius = ecalPars.rMin; - mOuterRadius = ecalPars.rMax; - mLength = ecalPars.zLength; - mEnableEndcap = ecalPars.enableFwdEndcap; } +//============================================================================== Detector::~Detector() { if (mHits) { @@ -55,190 +50,344 @@ Detector::~Detector() } } +//============================================================================== void Detector::ConstructGeometry() { createMaterials(); createGeometry(); + defineSamplingFactor(); } +//============================================================================== +void Detector::defineSamplingFactor() +{ + TString mcname = TVirtualMC::GetMC()->GetName(); + TString mctitle = TVirtualMC::GetMC()->GetTitle(); + LOGP(info, "Defining sampling factor for mc={}' and title='{}'", mcname.Data(), mctitle.Data()); + if (mcname.Contains("Geant3")) { + mSamplingFactorTransportModel = 0.983; + } else if (mcname.Contains("Geant4")) { + mSamplingFactorTransportModel = 1.; + } +} + +//============================================================================== void Detector::createMaterials() { - int ifield = 2; // ? - float fieldm = 10.0; // ? + LOGP(info, "Creating materials for ECL"); + + // Air + float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948}; + float zAir[4] = {6., 7., 8., 18.}; + float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827}; + float dAir = 1.20479E-3; + Mixture(0, "Air", aAir, zAir, dAir, 4, wAir); + + // Pb + Material(1, "Pb", 207.2, 82, 11.35, 0.56, 0., nullptr, 0); + + // Polysterene scintillator (CH) + float aP[2] = {12.011, 1.00794}; + float zP[2] = {6.0, 1.0}; + float wP[2] = {1.0, 1.0}; + float dP = 1.032; + Mixture(2, "Scintillator", aP, zP, dP, -2, wP); + + // Al + Material(3, "Al", 26.98, 13., 2.7, 8.9, 999., nullptr, 0); + + // PWO crystals + float aX[3] = {207.19, 183.85, 16.0}; + float zX[3] = {82.0, 74.0, 8.0}; + float wX[3] = {1.0, 1.0, 4.0}; + float dX = 8.28; + Mixture(4, "PbWO4", aX, zX, dX, -3, wX); + + int ifield = 2; // magnetic field flag + float fieldm = 10.; // maximum field value (in Kilogauss) + float deemax = 0.1; // maximum fractional energy loss in one step (0 < deemax <=1) + float tmaxfd = 10.; // maximum angle due to field permitted in one step (in degrees) o2::base::Detector::initFieldTrackingParams(ifield, fieldm); - - float tmaxfdLead = 0.1; // .10000E+01; // Degree - float stemaxLead = .10000E+01; // cm - float deemaxLead = 0.1; // 0.30000E-02; // Fraction of particle's energy 0clear(); + } + mSuperParentIndices.clear(); + currentTrackId = -1; + superparentId = -1; } -void Detector::EndOfEvent() { Reset(); } - +//============================================================================== void Detector::Register() { // This will create a branch in the output tree called Hit, setting the last // parameter to kFALSE means that this collection will not be written to the file, // it will exist only during the simulation - + LOGP(info, "Registering hits"); if (FairRootManager::Instance()) { FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true); } } +//============================================================================== void Detector::createGeometry() { LOGP(info, "Creating ECal geometry"); - TGeoManager* geoManager = gGeoManager; - TGeoVolume* vALIC = geoManager->GetVolume("barrel"); + TGeoVolume* vALIC = gGeoManager->GetVolume("barrel"); if (!vALIC) { LOGP(fatal, "Could not find barrel volume while constructing ECal geometry"); } new TGeoVolumeAssembly(GeometryTGeo::getECalVolPattern()); - TGeoVolume* vECal = geoManager->GetVolume(GeometryTGeo::getECalVolPattern()); + TGeoVolume* vECal = gGeoManager->GetVolume(GeometryTGeo::getECalVolPattern()); vALIC->AddNode(vECal, 2, new TGeoTranslation(0, 30., 0)); + vECal->SetTitle("ECalVol"); + + TGeoMedium* medAir = gGeoManager->GetMedium("ECL_Air"); + TGeoMedium* medPb = gGeoManager->GetMedium("ECL_Pb"); + TGeoMedium* medAl = gGeoManager->GetMedium("ECL_Al"); + TGeoMedium* medSc = gGeoManager->GetMedium("ECL_Scintillator"); + TGeoMedium* medPWO = gGeoManager->GetMedium("ECL_Crystal"); + + // Get relevant parameters + auto& pars = ECalBaseParam::Instance(); + auto& geo = Geometry::instance(); + + double rMin = pars.rMin; + double rMax = pars.rMax; + double layerThickness = pars.pbLayerThickness + pars.scLayerThickness; + double samplingModL = pars.frontPlateThickness + layerThickness * pars.nSamplingLayers - pars.pbLayerThickness; + double crystalAlpha = geo.getCrystalAlpha(); + double samplingAlpha = geo.getSamplingAlpha(); + double tanCrystalAlpha = std::tan(crystalAlpha); + double tanSamplingAlpha = std::tan(samplingAlpha); + + double sectorL = rMax - rMin; + double crystalThetaMin = geo.getFrontFaceCenterTheta(pars.nCrystalModulesZ - 1) - crystalAlpha; + double crystalHlMin = geo.getFrontFaceZatMinR(pars.nCrystalModulesZ - 1); + double crystalHlMax = crystalHlMin + sectorL / std::tan(crystalThetaMin); + double crystalHwMin = geo.getCrystalModW() / 2.; + double crystalHwMax = crystalHwMin * rMax / rMin; + auto crystalSectorShape = new TGeoTrap(sectorL / 2., 0, 0, crystalHlMin, crystalHwMin, crystalHwMin, 0, crystalHlMax, crystalHwMax, crystalHwMax, 0); + auto crystalSectorVolume = new TGeoVolume("crystalSectorVolume", crystalSectorShape, medAir); + AddSensitiveVolume(crystalSectorVolume); + crystalSectorVolume->SetLineColor(kCyan + 1); + crystalSectorVolume->SetTransparency(0); + + double samplingThetaAtMinZ = geo.getFrontFaceCenterTheta(pars.nCrystalModulesZ) + samplingAlpha; + double samplingThetaAtMaxZ = geo.getFrontFaceCenterTheta(pars.nCrystalModulesZ + pars.nSamplingModulesZ - 1) - samplingAlpha; + double samplingMinZatMinR = geo.getFrontFaceZatMinR(pars.nCrystalModulesZ) - geo.getSamplingModW() / std::sin(samplingThetaAtMinZ); + double samplingMaxZatMinR = geo.getFrontFaceZatMinR(pars.nCrystalModulesZ + pars.nSamplingModulesZ - 1); + double samplingMinZatMaxR = samplingMinZatMinR + sectorL / std::tan(samplingThetaAtMinZ); + double samplingMaxZatMaxR = samplingMaxZatMinR + sectorL / std::tan(samplingThetaAtMaxZ); + double hlMin = (samplingMaxZatMinR - samplingMinZatMinR) / 2.; + double hlMax = (samplingMaxZatMaxR - samplingMinZatMaxR) / 2.; + double zCenterMin = (samplingMaxZatMinR + samplingMinZatMinR) / 2.; + double zCenterMax = (samplingMaxZatMaxR + samplingMinZatMaxR) / 2.; + double zCenter = (zCenterMax + zCenterMin) / 2.; + double thetaCenter = std::atan((zCenterMax - zCenterMin) / sectorL) * TMath::RadToDeg(); + double samplingHwMin = geo.getSamplingModW() / 2.; + double samplingHwMax = samplingHwMin * rMax / rMin; + auto samplingSectorShape = new TGeoTrap(sectorL / 2., thetaCenter, 90, hlMin, samplingHwMin, samplingHwMin, 0, hlMax, samplingHwMax, samplingHwMax, 0); + auto samplingSectorVolume = new TGeoVolume("samplingSectorVolume", samplingSectorShape, medAir); + AddSensitiveVolume(samplingSectorVolume); + samplingSectorVolume->SetLineColor(kBlue + 1); + samplingSectorVolume->SetTransparency(0); + + double sectorR = rMin + sectorL / 2.; + for (int ism = 0; ism < pars.nSuperModules; ism++) { + // crystal + for (int i = 0; i < pars.nCrystalModulesPhi; i++) { + int row = ism * pars.nCrystalModulesPhi + i; + double phi0 = geo.getFrontFaceCenterCrystalPhi(row); + double x = sectorR * std::cos(phi0); + double y = sectorR * std::sin(phi0); + auto rot = new TGeoRotation(Form("ecalcrystalsecrot%d", row), 90 + phi0 * TMath::RadToDeg(), 90, 0); + vECal->AddNode(crystalSectorVolume, row, new TGeoCombiTrans(x, y, 0., rot)); + } + // sampling + for (int i = 0; i < pars.nSamplingModulesPhi; i++) { + int row = ism * pars.nSamplingModulesPhi + i; + double phi0 = geo.getFrontFaceCenterSamplingPhi(row); + double x = sectorR * std::cos(phi0); + double y = sectorR * std::sin(phi0); + auto rot1 = new TGeoRotation(Form("ecalsamplingsec1rot%d", row), 90 + phi0 * TMath::RadToDeg(), 90, 0.); + auto rot2 = new TGeoRotation(Form("ecalsamplingsec2rot%d", row), 90 + phi0 * TMath::RadToDeg(), 90, 180); + vECal->AddNode(samplingSectorVolume, 2 * row + 0, new TGeoCombiTrans(x, y, zCenter, rot1)); + vECal->AddNode(samplingSectorVolume, 2 * row + 1, new TGeoCombiTrans(x, y, -zCenter, rot2)); + } + } - char vstrng[100] = "ECalVol"; - vECal->SetTitle(vstrng); + for (int m = 0; m < pars.nCrystalModulesZ; m++) { + double tanBeta = geo.getTanBeta(m); + double dx1 = crystalHwMin; + double dx2 = crystalHwMin + pars.crystalModuleLength * tanCrystalAlpha; + double dy1 = crystalHwMin; + double dy2 = crystalHwMin + pars.crystalModuleLength * tanBeta; + double dz = pars.crystalModuleLength / 2.; + auto crystalModuleShape = new TGeoTrd2(dx1, dx2, dy1, dy2, dz); + auto crystalModuleVolume = new TGeoVolume(Form("crystalmodule%d", m), crystalModuleShape, medPWO); + AddSensitiveVolume(crystalModuleVolume); + crystalModuleVolume->SetLineColor(kCyan + 1); + crystalModuleVolume->SetTransparency(0); + double theta = geo.getFrontFaceCenterTheta(m); + double r = geo.getFrontFaceCenterR(m); + double z = geo.getFrontFaceCenterZ(m); + ROOT::Math::XYPoint pFrontFace(z, r - sectorR); + ROOT::Math::Polar2DVector vFrontFaceToCenter(dz, theta); + ROOT::Math::XYPoint pc = pFrontFace + vFrontFaceToCenter; + auto rot1 = new TGeoRotation(Form("ecalcrystalrot%d", 2 * m), 0, 270 + theta * TMath::RadToDeg(), 90); + crystalSectorVolume->AddNode(crystalModuleVolume, 2 * m, new TGeoCombiTrans(0, pc.x(), pc.y(), rot1)); + auto rot2 = new TGeoRotation(Form("ecalcrystalrot%d", 2 * m + 1), 0, 90 - theta * TMath::RadToDeg(), 90); + crystalSectorVolume->AddNode(crystalModuleVolume, 2 * m + 1, new TGeoCombiTrans(0, -pc.x(), pc.y(), rot2)); + } - // Build the ECal cylinder - auto& matmgr = o2::base::MaterialManager::Instance(); - TGeoMedium* medPb = matmgr.getTGeoMedium("ECL_LEAD"); - TGeoTube* ecalShape = new TGeoTube("ECLsh", mInnerRadius, mOuterRadius, mLength); - TGeoVolume* ecalVol = new TGeoVolume("ECL", ecalShape, medPb); - ecalVol->SetLineColor(kAzure - 9); - ecalVol->SetTransparency(0); - vECal->AddNode(ecalVol, 1, nullptr); + for (int m = 0; m < pars.nSamplingModulesZ; m++) { + int k = pars.nCrystalModulesZ + m; + double tanBeta = geo.getTanBeta(k); + double dx1 = samplingHwMin; + double dx2 = samplingHwMin + samplingModL * tanSamplingAlpha; + double dy1 = samplingHwMin; + double dy2 = samplingHwMin + samplingModL * tanBeta; + double dz = samplingModL / 2.; + auto samplingModuleShape = new TGeoTrd2(dx1, dx2, dy1, dy2, dz); + auto samplingModuleVolume = new TGeoVolume(Form("samplingmodule%d", m), samplingModuleShape, medSc); + AddSensitiveVolume(samplingModuleVolume); + samplingModuleVolume->SetLineColor(kAzure - 9); + samplingModuleVolume->SetTransparency(0); + double theta = geo.getFrontFaceCenterTheta(k); + double r = geo.getFrontFaceCenterR(k); + double z = geo.getFrontFaceCenterZ(k); + ROOT::Math::XYPoint pFrontFace(z - zCenter, r - sectorR); + ROOT::Math::Polar2DVector vFrontFaceToCenter(dz, theta); + ROOT::Math::XYPoint pc = pFrontFace + vFrontFaceToCenter; + auto rot1 = new TGeoRotation(Form("ecalsamplingrot%d", m), 0, 270 + theta * TMath::RadToDeg(), 90); + samplingSectorVolume->AddNode(samplingModuleVolume, m, new TGeoCombiTrans(0, pc.x(), pc.y(), rot1)); + + // adding front aluminium plate into the volume + double fdx1 = dx1; + double fdx2 = dx1 + pars.frontPlateThickness * tanSamplingAlpha; + double fdy1 = dy1; + double fdy2 = fdy1 + pars.frontPlateThickness * tanBeta; + double fdz = pars.frontPlateThickness / 2.; + auto frontShape = new TGeoTrd2(fdx1, fdx2, fdy1, fdy2, fdz); + auto frontVolume = new TGeoVolume(Form("front%d", m), frontShape, medAl); + samplingModuleVolume->AddNode(frontVolume, 0, new TGeoTranslation(0., 0., -dz + pars.frontPlateThickness / 2.)); + AddSensitiveVolume(frontVolume); + frontVolume->SetLineColor(kAzure - 7); + frontVolume->SetTransparency(0); + + // adding lead plates + for (int i = 0; i < pars.nSamplingLayers - 1; i++) { + double lz1 = pars.frontPlateThickness + pars.scLayerThickness + layerThickness * i; + double lz2 = lz1 + pars.pbLayerThickness; + double lzc = -dz + (lz1 + lz2) / 2.; + double ldx1 = dx1 + lz1 * tanSamplingAlpha; + double ldx2 = dx1 + lz2 * tanSamplingAlpha; + double ldy1 = dy1 + lz1 * tanBeta; + double ldy2 = dy1 + lz2 * tanBeta; + double ldz = pars.pbLayerThickness / 2.; + auto leadShape = new TGeoTrd2(ldx1, ldx2, ldy1, ldy2, ldz); + auto leadVolume = new TGeoVolume(Form("lead%d_%d", m, i), leadShape, medPb); + samplingModuleVolume->AddNode(leadVolume, i, new TGeoTranslation(0., 0., lzc)); + AddSensitiveVolume(leadVolume); + leadVolume->SetLineColor(kAzure - 7); + leadVolume->SetTransparency(0); + } + } - if (mEnableEndcap) { + if (pars.enableFwdEndcap) { // Build the ecal endcap - TGeoTube* ecalEndcapShape = new TGeoTube("ECLECsh", 15.f, 160.f, 0.5 * (mOuterRadius - mInnerRadius)); + TGeoTube* ecalEndcapShape = new TGeoTube("ECLECsh", 15.f, 160.f, 0.5 * (rMax - rMin)); TGeoVolume* ecalEndcapVol = new TGeoVolume("ECLEC", ecalEndcapShape, medPb); ecalEndcapVol->SetLineColor(kAzure - 9); ecalEndcapVol->SetTransparency(0); vECal->AddNode(ecalEndcapVol, 1, new TGeoTranslation(0, 0, -450.f)); } + // gGeoManager->CloseGeometry(); + // gGeoManager->CheckOverlaps(0.0001); } -void Detector::Reset() -{ - if (!o2::utils::ShmManager::Instance().isOperational()) { - mHits->clear(); - } -} - +//============================================================================== bool Detector::ProcessHits(FairVolume* vol) { - // This method is called from the MC stepping - if (!(fMC->TrackCharge())) { - return false; - } - - int lay = vol->getVolumeId(); - int volID = vol->getMCid(); - - // Is it needed to keep a track reference when the outer ITS volume is encountered? + LOGP(debug, "Processing hits"); auto stack = (o2::data::Stack*)fMC->GetStack(); - if (fMC->IsTrackExiting() && (lay == 0)) { - o2::TrackReference tr(*fMC, GetDetId()); - tr.setTrackID(stack->GetCurrentTrackNumber()); - tr.setUserId(lay); - stack->addTrackReference(tr); - } - bool startHit = false, stopHit = false; - unsigned char status = 0; - if (fMC->IsTrackEntering()) { - status |= Hit::kTrackEntering; - } - if (fMC->IsTrackInside()) { - status |= Hit::kTrackInside; - } - if (fMC->IsTrackExiting()) { - status |= Hit::kTrackExiting; - } - if (fMC->IsTrackOut()) { - status |= Hit::kTrackOut; - } - if (fMC->IsTrackStop()) { - status |= Hit::kTrackStopped; - } - if (fMC->IsTrackAlive()) { - status |= Hit::kTrackAlive; + int trackId = stack->GetCurrentTrackNumber(); + int parentId = stack->GetCurrentParentTrackNumber(); + + if (trackId != currentTrackId) { + auto superparentIndexIt = mSuperParentIndices.find(parentId); + if (superparentIndexIt != mSuperParentIndices.end()) { + superparentId = superparentIndexIt->second; + mSuperParentIndices[trackId] = superparentIndexIt->second; + } else { + // for new incoming tracks the superparent index is equal to the track ID (for recursion) + mSuperParentIndices[trackId] = trackId; + superparentId = trackId; + } + currentTrackId = trackId; } - // track is entering or created in the volume - if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) { - startHit = true; - } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) { - stopHit = true; + double eloss = fMC->Edep(); + if (eloss < DBL_EPSILON) { + return false; } - // increment energy loss at all steps except entrance - if (!startHit) { - mTrackData.mEnergyLoss += fMC->Edep(); - } - if (!(startHit | stopHit)) { - return false; // do noting + TString volName = vol->GetName(); + bool isCrystal = volName.Contains("crystalmodule"); + bool isSampling = volName.Contains("samplingmodule"); + + if (!isCrystal && !isSampling) { + return false; } - if (startHit) { - mTrackData.mEnergyLoss = 0.; - fMC->TrackMomentum(mTrackData.mMomentumStart); - fMC->TrackPosition(mTrackData.mPositionStart); - mTrackData.mTrkStatusStart = status; - mTrackData.mHitStarted = true; + if (isCrystal) { + LOGP(debug, "Processing crystal {}", volName.Data()); + } else { + eloss *= mSamplingFactorTransportModel; + LOGP(debug, "Processing scintillator {}", volName.Data()); } - if (stopHit) { - TLorentzVector positionStop; - fMC->TrackPosition(positionStop); - // Retrieve the indices with the volume path - int stave(0), halfstave(0), chipinmodule(0), module; - fMC->CurrentVolOffID(1, chipinmodule); - fMC->CurrentVolOffID(2, module); - fMC->CurrentVolOffID(3, halfstave); - fMC->CurrentVolOffID(4, stave); - - Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), - mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), - mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); - // p->SetTotalEnergy(vmc->Etot()); - - // RS: not sure this is needed - // Increment number of Detector det points in TParticle + int sectorId, moduleId; + fMC->CurrentVolID(moduleId); + fMC->CurrentVolOffID(1, sectorId); + int cellID = Geometry::instance().getCellID(moduleId, sectorId, isCrystal); + LOGP(debug, "isCrystal={} sectorId={} moduleId={} cellID={} eloss={}", isCrystal, sectorId, moduleId, cellID, eloss); + + int trackID = superparentId; + auto hit = std::find_if(mHits->begin(), mHits->end(), [cellID, trackID](const Hit& hit) { return hit.GetTrackID() == trackID && hit.GetCellID() == cellID; }); + if (hit == mHits->end()) { + float posX, posY, posZ, momX, momY, momZ, energy; + fMC->TrackPosition(posX, posY, posZ); + fMC->TrackMomentum(momX, momY, momZ, energy); + auto pos = math_utils::Point3D(posX, posY, posZ); + auto mom = math_utils::Vector3D(momX, momY, momZ); + float time = fMC->TrackTime() * 1e9; // time in ns + mHits->emplace_back(trackID, cellID, pos, mom, time, eloss); stack->addHit(GetDetId()); + } else { + hit->SetEnergyLoss(hit->GetEnergyLoss() + eloss); } - return true; } -o2::itsmft::Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, - const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus, - unsigned char endStatus) -{ - mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus); - return &(mHits->back()); -} - } // namespace ecal } // namespace o2 -ClassImp(o2::ecal::Detector); \ No newline at end of file +ClassImp(o2::ecal::Detector); diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx new file mode 100644 index 0000000000000..42c1908a29d18 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx @@ -0,0 +1,91 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Digitizer.cxx +/// \brief Digitization of ECal MC information +/// +/// \author Evgeny Kryshen + +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2::ecal; + +//============================================================================== +Digitizer::Digitizer() +{ + auto& geo = Geometry::instance(); + mArrayD.resize(geo.getNrows() * geo.getNcols()); +} + +//============================================================================== +void Digitizer::processHits(const std::vector* hits, std::vector& digits, o2::dataformats::MCTruthContainer& labels, int collId) +{ + digits.clear(); + labels.clear(); + + LOGP(debug, "nHits = {}", hits->size()); + auto& geo = Geometry::instance(); + + for (int i = 0; i < mArrayD.size(); i++) { + mArrayD[i].setAmplitude(0); + mArrayD[i].setTimeStamp(1000); + mArrayD[i].setTower(i); + mArrayD[i].setLabel(-1); + // TODO: simulate noise + } + + for (auto& hit : *hits) { + int cellID = hit.GetCellID(); + double eloss = hit.GetEnergyLoss(); + double t = hit.GetTime(); + double elossSmeared = eloss; + bool isCrystal = geo.isCrystal(cellID); + if (isCrystal) { // crystal + double elossSmearedNpe = gRandom->Poisson(eloss * mCrystalPePerGeV) / mCrystalPePerGeV; + if (mSmearCrystal) { + elossSmeared = elossSmearedNpe * gRandom->Gaus(1, 0.007); // light attenuation in crystals + } + } else { // sampling + elossSmeared *= mSamplingFraction; + } + + Digit& digit = mArrayD[cellID]; + digit.setAmplitude(digit.getAmplitude() + elossSmeared); + if (t < digit.getTimeStamp()) { + digit.setTimeStamp(t); // setting earliest time, TODO: add time smearing + } + LOGF(debug, " crystal: %d cellID = %5d, eloss = %8.5f elossSmeared = %8.5f time = %8.5f", isCrystal, cellID, eloss, elossSmeared, t); + + // Adding MC info + MCLabel label(hit.GetTrackID(), collId, 0, false, hit.GetEnergyLoss()); + int labelIndex = digit.getLabel(); + if (labelIndex == -1) { + labelIndex = labels.getIndexedSize(); + labels.addElement(labelIndex, label); + digit.setLabel(labelIndex); + } else { + labels.addElementRandomAccess(labelIndex, label); + } + } // hits + + for (int i = 0; i < mArrayD.size(); i++) { + if (mArrayD[i].getAmplitude() > mThreshold) { + digits.push_back(mArrayD[i]); + } + } +} diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h index 167342773f196..5d7383f086362 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h @@ -17,5 +17,6 @@ #pragma link C++ class o2::ecal::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::ecal::Detector> + ; +#pragma link C++ class o2::ecal::Digitizer + ; #endif diff --git a/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt new file mode 100644 index 0000000000000..d9ea4b632952c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2025 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(base) +add_subdirectory(simulation) diff --git a/Detectors/Upgrades/ALICE3/FD3/README.md b/Detectors/Upgrades/ALICE3/FD3/README.md new file mode 100644 index 0000000000000..54c1fd37b2590 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/README.md @@ -0,0 +1,10 @@ + + +# ALICE 3 FORWARD DETECTOR + +This is top page for the FD3 detector documentation. + + diff --git a/Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt new file mode 100644 index 0000000000000..c76665da1344f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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. + +o2_add_library(FD3Base + SOURCES src/GeometryTGeo.cxx + src/FD3BaseParam.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsBase) + +o2_target_root_dictionary(FD3Base + HEADERS include/FD3Base/GeometryTGeo.h + include/FD3Base/Constants.h + include/FD3Base/FD3BaseParam.h) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h similarity index 51% rename from Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h rename to Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h index ec45e6587a974..428a7a1f6d179 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h @@ -8,34 +8,31 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// -/// \file Label.h -/// \brief -/// -#ifndef TRACKINGITSU_INCLUDE_LABEL_H_ -#define TRACKINGITSU_INCLUDE_LABEL_H_ +/// \file Constants.h +/// \brief General constants in FV0 +/// +/// \author Maciej Slupecki, University of Jyvaskyla, Finland -#include +#ifndef ALICEO2_FD3_CONSTANTS_ +#define ALICEO2_FD3_CONSTANTS_ namespace o2 { -namespace its +namespace fd3 { +struct Constants { + static constexpr unsigned int nsect = 8; + static constexpr unsigned int nringsA = 5; + static constexpr unsigned int nringsC = 6; -struct Label final { - Label(const int, const float, const float, const float, const int, const int); + static constexpr float etaMax = 7.0f; + static constexpr float etaMin = 4.0f; - int monteCarloId; - float transverseMomentum; - float phi; - float pseudorapidity; - int pdgCode; - int numberOfClusters; - - friend std::ostream& operator<<(std::ostream&, const Label&); + static constexpr unsigned int nringsA_withMG = 3; + static constexpr float etaMinA_withMG = 5.0f; }; -} // namespace its -} // namespace o2 -#endif /* TRACKINGITSU_INCLUDE_LABEL_H_ */ +} // namespace fd3 +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.h new file mode 100644 index 0000000000000..9836cebbfa760 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.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 ALICEO2_FD3_FD3BASEPARAM_ +#define ALICEO2_FD3_FD3BASEPARAM_ + +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/Constants.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace fd3 +{ +struct FD3BaseParam : public o2::conf::ConfigurableParamHelper { + + float zmodA = 1700.0f; + float zmodC = -1850.0f; + float dzscint = 4.0f; + + bool withMG = false; // modified geometry with 3 rings on A side + + bool plateBehindA = false; + bool fullContainer = false; + float dzplate = 1.0f; // Aluminium plate width + + O2ParamDef(FD3BaseParam, "FD3Base"); +}; + +} // namespace fd3 +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h new file mode 100644 index 0000000000000..0e38bd4ccd21f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FD3_GEOMETRYTGEO_H_ +#define ALICEO2_FD3_GEOMETRYTGEO_H_ + +#include + +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace fd3 +{ + +/// FD3 Geometry type +class GeometryTGeo : public o2::detectors::DetMatrixCache +{ + public: + GeometryTGeo(bool build = false, int loadTrans = 0); + + void Build(int loadTrans); + void fillMatrixCache(int mask); + virtual ~GeometryTGeo(); + + static GeometryTGeo* Instance(); + + void getGlobalPosition(float& x, float& y, float& z); + + static constexpr o2::detectors::DetID::ID getDetID() { return o2::detectors::DetID::FD3; } + + private: + static std::unique_ptr sInstance; + + ClassDefNV(GeometryTGeo, 1); +}; +} // namespace fd3 +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h new file mode 100644 index 0000000000000..8475ef2c77313 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h @@ -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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fd3::Constants + ; +#pragma link C++ class o2::fd3::GeometryTGeo + ; +#pragma link C++ class o2::fd3::FD3BaseParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::fd3::FD3BaseParam> + ; + +#endif diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx similarity index 88% rename from Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx rename to Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx index b2ef17e540112..74b45962b3f39 100644 --- a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx @@ -9,5 +9,6 @@ // 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; +#include "FD3Base/FD3BaseParam.h" + +O2ParamImpl(o2::fd3::FD3BaseParam); diff --git a/Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx new file mode 100644 index 0000000000000..16788cb8944e3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx @@ -0,0 +1,66 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/FD3BaseParam.h" + +#include + +#include + +using namespace o2::fd3; +namespace o2 +{ +namespace fd3 +{ + +std::unique_ptr GeometryTGeo::sInstance; + +GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : DetMatrixCache() +{ + if (sInstance) { + LOGP(fatal, "Invalid use of public constructor: o2::fd3::GeometryTGeo instance exists"); + } + if (build) { + Build(loadTrans); + } +} + +GeometryTGeo::~GeometryTGeo() = default; + +GeometryTGeo* GeometryTGeo::Instance() +{ + if (!sInstance) { + sInstance = std::unique_ptr(new GeometryTGeo(true, 0)); + } + return sInstance.get(); +} + +void GeometryTGeo::Build(int loadTrans) +{ + if (isBuilt()) { + LOGP(warning, "Already built"); + return; // already initialized + } + + if (!gGeoManager) { + LOGP(fatal, "Geometry is not loaded"); + } + + fillMatrixCache(loadTrans); +} + +void GeometryTGeo::fillMatrixCache(int mask) +{ +} + +} // namespace fd3 +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt new file mode 100644 index 0000000000000..38886ec5fbe07 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(FD3Simulation + SOURCES src/Detector.cxx + PUBLIC_LINK_LIBRARIES O2::FD3Base + O2::DataFormatsFD3 + ROOT::Physics) + +o2_target_root_dictionary(FD3Simulation + HEADERS include/FD3Simulation/Detector.h) diff --git a/Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h new file mode 100644 index 0000000000000..2d17acbd4a0e8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h @@ -0,0 +1,150 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Detector.h +/// \brief Definition of the Detector class + +#ifndef ALICEO2_FD3_DETECTOR_H_ +#define ALICEO2_FD3_DETECTOR_H_ + +#include "SimulationDataFormat/BaseHits.h" +#include "DetectorsBase/Detector.h" +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/FD3BaseParam.h" +#include "DataFormatsFD3/Hit.h" +#include "Rtypes.h" +#include "TGeoManager.h" +#include "TLorentzVector.h" +#include "TVector3.h" +#include + +class FairVolume; +class TGeoVolume; + +namespace o2 +{ +namespace fd3 +{ +class GeometryTGeo; +} +} // namespace o2 + +namespace o2 +{ +namespace fd3 +{ + +class Detector : public o2::base::DetImpl +{ + public: + Detector(bool Active); + + Detector() = default; + + ~Detector() override; + + void ConstructGeometry() override; + + /// This method is an example of how to add your own point of type Hit to the clones array + o2::fd3::Hit* addHit(int trackId, unsigned 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); + // unsigned int startStatus, + // unsigned int endStatus); + + std::vector* getHits(Int_t iColl) + { + if (iColl == 0) { + return mHits; + } + return nullptr; + } + + // Mandatory overrides + void BeginPrimary() override { ; } + void FinishPrimary() override { ; } + void InitializeO2Detector() override; + void PostTrack() override { ; } + void PreTrack() override { ; } + bool ProcessHits(FairVolume* v = nullptr) override; + void EndOfEvent() override; + void Register() override; + void Reset() override; + + void createMaterials(); + void buildModules(); + + enum EMedia { + Scintillator, + Aluminium + }; + + private: + Detector(const Detector&); + Detector& operator=(const Detector&); + + std::vector* mHits = nullptr; + GeometryTGeo* mGeometryTGeo = nullptr; + + TGeoVolumeAssembly* buildModuleA(); + TGeoVolumeAssembly* buildModuleC(); + + float ringSize(float zmod, float eta); + + unsigned int mNumberOfRingsA, mNumberOfRingsC, mNumberOfSectors; + float mDzScint, mDzPlate; + + std::vector mRingSizesA = {}, mRingSizesC = {}; + + float mEtaMaxA, mEtaMaxC, mEtaMinA, mEtaMinC; + float mZA, mZC; + + bool mPlateBehindA, mFullContainer; + + void defineSensitiveVolumes(); + void definePassiveVolumes(); + + /// Transient data about track passing the sensor, needed by ProcessHits() + struct TrackData { // this is transient + bool mHitStarted; //! hit creation started + unsigned char mTrkStatusStart; //! track status flag + TLorentzVector mPositionStart; //! position at entrance + TLorentzVector mMomentumStart; //! momentum + double mEnergyLoss; //! energy loss + } mTrackData; //! + + template + friend class o2::base::DetImpl; + ClassDefOverride(Detector, 1); +}; + +// Input and output function for standard C++ input/output. +std::ostream& operator<<(std::ostream& os, Detector& source); +std::istream& operator>>(std::istream& os, Detector& source); + +} // namespace fd3 +} // namespace o2 + +#ifdef USESHM +namespace o2 +{ +namespace base +{ +template <> +struct UseShm { + static constexpr bool value = true; +}; +} // namespace base +} // namespace o2 +#endif +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FD3/simulation/src/Detector.cxx new file mode 100644 index 0000000000000..bd79b1deaad80 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/src/Detector.cxx @@ -0,0 +1,413 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Detector.cxx +/// \brief Implementation of the Detector class + +#include "DataFormatsFD3/Hit.h" +#include "FD3Simulation/Detector.h" +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/FD3BaseParam.h" +#include "FD3Base/Constants.h" + +#include "DetectorsBase/Stack.h" +#include "SimulationDataFormat/TrackReference.h" +#include "Field/MagneticField.h" + +// FairRoot includes +#include "FairDetector.h" +#include +#include "FairRootManager.h" +#include "FairRun.h" +#include "FairRuntimeDb.h" +#include "FairVolume.h" +#include "FairRootManager.h" + +#include "TVirtualMC.h" +#include "TLorentzVector.h" +#include "TVector3.h" +#include +#include +#include +#include +#include +#include +#include "TRandom.h" +#include + +class FairModule; + +class TGeoMedium; + +using namespace o2::fd3; +using o2::fd3::Hit; + +Detector::Detector(bool active) + : o2::base::DetImpl("FD3", true), + mHits(o2::utils::createSimVector()), + mGeometryTGeo(nullptr), + mTrackData() +{ + mNumberOfRingsC = Constants::nringsC; + mNumberOfSectors = Constants::nsect; + + mEtaMinA = Constants::etaMin; + mEtaMaxA = Constants::etaMax; + mEtaMinC = -Constants::etaMax; + mEtaMaxC = -Constants::etaMin; + + auto& baseParam = FD3BaseParam::Instance(); + + if (baseParam.withMG) { + mNumberOfRingsA = Constants::nringsA_withMG; + mEtaMinA = Constants::etaMinA_withMG; + } else { + mNumberOfRingsA = Constants::nringsA; + mEtaMinA = Constants::etaMin; + } + + mDzScint = baseParam.dzscint / 2; + mDzPlate = baseParam.dzplate; + + mPlateBehindA = baseParam.plateBehindA; + mFullContainer = baseParam.fullContainer; + + mZA = baseParam.zmodA; + mZC = baseParam.zmodC; + + for (int i = 0; i <= mNumberOfRingsA + 1; i++) { + float eta = mEtaMaxA - i * (mEtaMaxA - mEtaMinA) / mNumberOfRingsA; + float r = ringSize(mZA, eta); + mRingSizesA.emplace_back(r); + } + + for (int i = 0; i <= mNumberOfRingsC + 1; i++) { + float eta = mEtaMinC + i * (mEtaMaxC - mEtaMinC) / mNumberOfRingsC; + float r = ringSize(mZC, eta); + mRingSizesC.emplace_back(r); + } +} + +Detector::Detector(const Detector& rhs) + : o2::base::DetImpl(rhs), + mTrackData(), + mHits(o2::utils::createSimVector()) +{ +} + +Detector& Detector::operator=(const Detector& rhs) +{ + + if (this == &rhs) { + return *this; + } + // base class assignment + base::Detector::operator=(rhs); + mTrackData = rhs.mTrackData; + + mHits = nullptr; + return *this; +} + +Detector::~Detector() +{ + + if (mHits) { + o2::utils::freeSimVector(mHits); + } +} + +void Detector::InitializeO2Detector() +{ + LOG(info) << "Initialize Forward Detector"; + mGeometryTGeo = GeometryTGeo::Instance(); + defineSensitiveVolumes(); +} + +bool Detector::ProcessHits(FairVolume* vol) +{ + // This method is called from the MC stepping + if (!(fMC->TrackCharge())) { + return kFALSE; + } + + int detId; + int volID = fMC->CurrentVolID(detId); + + auto stack = (o2::data::Stack*)fMC->GetStack(); + + // Check track status to define when hit is started and when it is stopped + int particlePdg = fMC->TrackPid(); + bool startHit = false, stopHit = false; + if ((fMC->IsTrackEntering()) || (fMC->IsTrackInside() && !mTrackData.mHitStarted)) { + startHit = true; + } else if ((fMC->IsTrackExiting() || fMC->IsTrackOut() || fMC->IsTrackStop())) { + stopHit = true; + } + + // increment energy loss at all steps except entrance + if (!startHit) { + mTrackData.mEnergyLoss += fMC->Edep(); + } + if (!(startHit | stopHit)) { + return kFALSE; // do noting + } + + if (startHit) { + mTrackData.mHitStarted = true; + mTrackData.mEnergyLoss = 0.; + fMC->TrackMomentum(mTrackData.mMomentumStart); + fMC->TrackPosition(mTrackData.mPositionStart); + mTrackData.mTrkStatusStart = true; + } + + if (stopHit) { + TLorentzVector positionStop; + fMC->TrackPosition(positionStop); + int trackId = stack->GetCurrentTrackNumber(); + + math_utils::Point3D posStart(mTrackData.mPositionStart.X(), mTrackData.mPositionStart.Y(), mTrackData.mPositionStart.Z()); + math_utils::Point3D posStop(positionStop.X(), positionStop.Y(), positionStop.Z()); + math_utils::Vector3D momStart(mTrackData.mMomentumStart.Px(), mTrackData.mMomentumStart.Py(), mTrackData.mMomentumStart.Pz()); + + Hit* p = addHit(trackId, detId, posStart, posStop, + momStart, mTrackData.mMomentumStart.E(), + positionStop.T(), mTrackData.mEnergyLoss, particlePdg); + stack->addHit(GetDetId()); + } else { + return false; // do nothing more + } + return true; +} + +o2::fd3::Hit* Detector::addHit(int trackId, unsigned 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) +{ + mHits->emplace_back(trackId, detId, startPos, + endPos, startMom, startE, endTime, eLoss, particlePdg); + return &(mHits->back()); +} + +void Detector::ConstructGeometry() +{ + createMaterials(); + buildModules(); +} + +void Detector::EndOfEvent() +{ + Reset(); +} + +void Detector::Register() +{ + // This will create a branch in the output tree called Hit, setting the last + // parameter to kFALSE means that this collection will not be written to the file, + // it will exist only during the simulation + + if (FairRootManager::Instance()) { + FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, kTRUE); + } +} + +void Detector::Reset() +{ + if (!o2::utils::ShmManager::Instance().isOperational()) { + mHits->clear(); + } +} + +void Detector::createMaterials() +{ + float density, as[11], zs[11], ws[11]; + double radLength, absLength, a_ad, z_ad; + int id; + + // EJ-204 scintillator, based on polyvinyltoluene + const int nScint = 2; + float aScint[nScint] = {1.00784, 12.0107}; + float zScint[nScint] = {1, 6}; + float wScint[nScint] = {0.07085, 0.92915}; // based on EJ-204 datasheet: n_atoms/cm3 + const float dScint = 1.023; + + // Aluminium + Float_t aAlu = 26.981; + Float_t zAlu = 13; + Float_t dAlu = 2.7; + + int matId = 0; // tmp material id number + const int unsens = 0, sens = 1; // sensitive or unsensitive medium + // + int fieldType = 3; // Field type + float maxField = 5.0; // Field max. + + float tmaxfd3 = -10.0; // max deflection angle due to magnetic field in one step + float stemax = 0.1; // max step allowed [cm] + float deemax = 1.0; // maximum fractional energy loss in one step 0GetVolume("cave"); + + if (!vCave) { + LOG(fatal) << "Could not find the top volume!"; + } + + TGeoVolumeAssembly* vFD3A = buildModuleA(); + TGeoVolumeAssembly* vFD3C = buildModuleC(); + + vCave->AddNode(vFD3A, 1, new TGeoTranslation(0., 0., mZA)); + vCave->AddNode(vFD3C, 2, new TGeoTranslation(0., 0., mZC)); +} + +TGeoVolumeAssembly* Detector::buildModuleA() +{ + TGeoVolumeAssembly* mod = new TGeoVolumeAssembly("FD3A"); + + const TGeoMedium* medium = gGeoManager->GetMedium("FD3_Scintillator"); + + float dphiDeg = 360. / mNumberOfSectors; + + for (int ir = 0; ir < mNumberOfRingsA; ir++) { + std::string rName = "fd3_ring" + std::to_string(ir + 1); + TGeoVolumeAssembly* ring = new TGeoVolumeAssembly(rName.c_str()); + float rmin = mRingSizesA[ir]; + float rmax = mRingSizesA[ir + 1]; + LOG(info) << "ring" << ir << ": from " << rmin << " to " << rmax; + for (int ic = 0; ic < mNumberOfSectors; ic++) { + int cellId = ic + mNumberOfSectors * ir; + std::string nodeName = "fd3_node" + std::to_string(cellId); + float phimin = dphiDeg * ic; + float phimax = dphiDeg * (ic + 1); + auto tbs = new TGeoTubeSeg("tbs", rmin, rmax, mDzScint, phimin, phimax); + auto nod = new TGeoVolume(nodeName.c_str(), tbs, medium); + if ((ir + ic) % 2 == 0) { + nod->SetLineColor(kRed); + } else { + nod->SetLineColor(kRed - 7); + } + ring->AddNode(nod, cellId); + } + mod->AddNode(ring, ir + 1); + } + + // Aluminium plates on one or both sides of the A side module + if (mPlateBehindA || mFullContainer) { + LOG(info) << "adding container on A side"; + auto pmed = (TGeoMedium*)gGeoManager->GetMedium("FD3_Aluminium"); + auto pvol = new TGeoTube("pvol_fd3a", mRingSizesA[0], mRingSizesA[mNumberOfRingsA], mDzPlate); + auto pnod1 = new TGeoVolume("pnod1_FD3A", pvol, pmed); + double dpz = 2. + mDzPlate / 2; + mod->AddNode(pnod1, 1, new TGeoTranslation(0, 0, dpz)); + + if (mFullContainer) { + auto pnod2 = new TGeoVolume("pnod2_FD3A", pvol, pmed); + mod->AddNode(pnod2, 1, new TGeoTranslation(0, 0, -dpz)); + } + } + return mod; +} + +TGeoVolumeAssembly* Detector::buildModuleC() +{ + TGeoVolumeAssembly* mod = new TGeoVolumeAssembly("FD3C"); + + const TGeoMedium* medium = gGeoManager->GetMedium("FD3_Scintillator"); + + float dphiDeg = 360. / mNumberOfSectors; + + for (int ir = 0; ir < mNumberOfRingsC; ir++) { + std::string rName = "fd3_ring" + std::to_string(ir + 1 + mNumberOfRingsA); + TGeoVolumeAssembly* ring = new TGeoVolumeAssembly(rName.c_str()); + float rmin = mRingSizesC[ir]; + float rmax = mRingSizesC[ir + 1]; + LOG(info) << "ring" << ir + mNumberOfRingsA << ": from " << rmin << " to " << rmax; + for (int ic = 0; ic < mNumberOfSectors; ic++) { + int cellId = ic + mNumberOfSectors * (ir + mNumberOfRingsA); + std::string nodeName = "fd3_node" + std::to_string(cellId); + float phimin = dphiDeg * ic; + float phimax = dphiDeg * (ic + 1); + auto tbs = new TGeoTubeSeg("tbs", rmin, rmax, mDzScint, phimin, phimax); + auto nod = new TGeoVolume(nodeName.c_str(), tbs, medium); + if ((ir + ic) % 2 == 0) { + nod->SetLineColor(kBlue); + } else { + nod->SetLineColor(kBlue - 7); + } + ring->AddNode(nod, cellId); + } + mod->AddNode(ring, ir + 1); + } + + // Aluminium plates on both sides of the C side module + if (mFullContainer) { + LOG(info) << "adding container on C side"; + auto pmed = (TGeoMedium*)gGeoManager->GetMedium("FD3_Aluminium"); + auto pvol = new TGeoTube("pvol_fd3c", mRingSizesC[0], mRingSizesC[mNumberOfRingsC], mDzPlate); + auto pnod1 = new TGeoVolume("pnod1_FD3C", pvol, pmed); + auto pnod2 = new TGeoVolume("pnod2_FD3C", pvol, pmed); + double dpz = mDzScint / 2 + mDzPlate / 2; + + mod->AddNode(pnod1, 1, new TGeoTranslation(0, 0, dpz)); + mod->AddNode(pnod2, 2, new TGeoTranslation(0, 0, -dpz)); + } + + return mod; +} + +void Detector::defineSensitiveVolumes() +{ + LOG(info) << "Adding FD3 Sentitive Volumes"; + + TGeoVolume* v; + TString volumeName; + + int nCellsA = mNumberOfRingsA * mNumberOfSectors; + int nCellsC = mNumberOfRingsC * mNumberOfSectors; + + LOG(info) << "number of A rings = " << mNumberOfRingsA << " number of cells = " << nCellsA; + LOG(info) << "number of C rings = " << mNumberOfRingsC << " number of cells = " << nCellsC; + + for (int iv = 0; iv < nCellsA + nCellsC; iv++) { + volumeName = "fd3_node" + std::to_string(iv); + v = gGeoManager->GetVolume(volumeName); + LOG(info) << "Adding sensitive volume => " << v->GetName(); + AddSensitiveVolume(v); + } +} + +float Detector::ringSize(float z, float eta) +{ + return z * TMath::Tan(2 * TMath::ATan(TMath::Exp(-eta))); +} + +ClassImp(o2::fd3::Detector); diff --git a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h similarity index 70% rename from DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx rename to Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h index 8affa29259f7a..83df03490ebd7 100644 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h @@ -9,11 +9,13 @@ // 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 +#ifdef __CLING__ -#include "DataFormatsEMCAL/EMCALChannelData.h" +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; -using namespace o2::dataformats; +#pragma link C++ class o2::fd3::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::fd3::Detector> + ; -ClassImp(o2::dataformats::EMCALChannelData; +#endif diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FT3/simulation/CMakeLists.txt index 89f8c23797fac..23414d4ae7269 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/CMakeLists.txt @@ -10,14 +10,18 @@ # or submit itself to any jurisdiction. o2_add_library(FT3Simulation - SOURCES src/FT3Layer.cxx + SOURCES + src/FT3Module.cxx + src/FT3Layer.cxx src/Detector.cxx PUBLIC_LINK_LIBRARIES O2::FT3Base O2::ITSMFTSimulation ROOT::Physics) o2_target_root_dictionary(FT3Simulation - HEADERS include/FT3Simulation/Detector.h + HEADERS + include/FT3Simulation/FT3Module.h + include/FT3Simulation/Detector.h include/FT3Simulation/FT3Layer.h) o2_data_file(COPY data DESTINATION Detectors/FT3/simulation) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h index a88ea5a351ad2..a68f8cf7788b6 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h @@ -116,6 +116,7 @@ class Detector : public o2::base::DetImpl void buildFT3V3b(); void buildFT3Scoping(); void buildFT3NewVacuumVessel(); + void buildFT3ScopingV3(); void buildFT3FromFile(std::string); GeometryTGeo* mGeometryTGeo; //! access to geometry details diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h index 7159f2a6d1d9f..44a0ef0f7d8bc 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h @@ -18,6 +18,7 @@ #include // for gGeoManager #include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc #include "FT3Simulation/Detector.h" // for Detector, Detector::Model +#include "FT3Simulation/FT3Module.h" class TGeoVolume; @@ -57,6 +58,24 @@ class FT3Layer : public TObject /// \param motherVolume the TGeoVolume owing the volume structure virtual void createLayer(TGeoVolume* motherVolume); + static void initialize_mat(); + + // create layer for disk support + void createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName); + void createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName); + + static TGeoMaterial* carbonFiberMat; + static TGeoMedium* medCarbonFiber; + + static TGeoMaterial* kaptonMat; + static TGeoMedium* kaptonMed; + + static TGeoMaterial* waterMat; + static TGeoMedium* waterMed; + + static TGeoMaterial* foamMat; + static TGeoMedium* medFoam; + private: Int_t mLayerNumber = -1; ///< Current layer number Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Module.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Module.h new file mode 100644 index 0000000000000..15ac6be995646 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Module.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. + +/// \file FT3Module.h +/// \brief Definition of the FT3Module class + +#ifndef FT3MODULE_H +#define FT3MODULE_H + +#include +#include + +class FT3Module +{ + + public: + static void initialize_materials(); + static TGeoMaterial* siliconMat; + static TGeoMedium* siliconMed; + static TGeoMaterial* copperMat; + static TGeoMedium* copperMed; + static TGeoMaterial* kaptonMat; + static TGeoMedium* kaptonMed; + static TGeoMaterial* epoxyMat; + static TGeoMedium* epoxyMed; + static TGeoMaterial* AluminumMat; + static TGeoMedium* AluminumMed; + + const char* mDetName; + + static void createModule(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume); + + private: + static void create_layout(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume); +}; + +#endif // FT3MODULE_H diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index ce132fdb33cd3..4b139272834f1 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -40,6 +40,8 @@ #include // for NULL, snprintf +#define MAX_SENSORS 2000 + class FairModule; class TGeoMedium; @@ -127,7 +129,8 @@ void Detector::buildFT3FromFile(std::string configFileName) //_________________________________________________________________________________________________ void Detector::exportLayout() { - // Export FT3 Layout description to file. One line per disk + // Export FT3 Layout description to file. + // One line per disk: // z_layer r_in r_out Layerx2X0 std::string configFileName = "FT3_layout.cfg"; @@ -344,6 +347,65 @@ void Detector::buildFT3NewVacuumVessel() } } +void Detector::buildFT3ScopingV3() +{ + // Build the FT3 detector according to v3 layout + // https://indico.cern.ch/event/1596309/contributions/6728167/attachments/3190117/5677220/2025-12-10-AW-ALICE3planning.pdf + // Middle disks inner radius 10 cm + // Outer disks inner radius 20 cm + + LOG(info) << "Building FT3 Detector: v3 scoping version"; + + mNumberOfLayers = 6; + float sensorThickness = 30.e-4; + float layersx2X0 = 1.e-2; + std::vector> layersConfigCSide{ + {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} + {100., 10.0, 35., layersx2X0}, + {122., 10.0, 35., layersx2X0}, + {150., 20.0, 68.f, layersx2X0}, + {180., 20.0, 68.f, layersx2X0}, + {220., 20.0, 68.f, layersx2X0}}; + + std::vector> layersConfigASide{ + {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} + {100., 10.0, 35., layersx2X0}, + {122., 10.0, 35., layersx2X0}, + {150., 20.0, 68.f, layersx2X0}, + {180., 20.0, 68.f, layersx2X0}, + {220., 20.0, 68.f, layersx2X0}}; + + mLayerName.resize(2); + mLayerName[0].resize(mNumberOfLayers); + mLayerName[1].resize(mNumberOfLayers); + mLayerID.clear(); + mLayers.resize(2); + + for (auto direction : {0, 1}) { + for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + std::string directionName = std::to_string(direction); + std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); + mLayerName[direction][layerNumber] = layerName; + float z, rIn, rOut, x0; + if (direction == 0) { // C-Side + z = layersConfigCSide[layerNumber][0]; + rIn = layersConfigCSide[layerNumber][1]; + rOut = layersConfigCSide[layerNumber][2]; + x0 = layersConfigCSide[layerNumber][3]; + } else if (direction == 1) { // A-Side + z = layersConfigASide[layerNumber][0]; + rIn = layersConfigASide[layerNumber][1]; + rOut = layersConfigASide[layerNumber][2]; + x0 = layersConfigASide[layerNumber][3]; + } + + LOG(info) << "Adding Layer " << layerName << " at z = " << z; + // Add layers + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + } + } +} + //_________________________________________________________________________________________________ void Detector::buildFT3Scoping() { @@ -409,7 +471,7 @@ Detector::Detector(bool active) } else { switch (ft3BaseParam.geoModel) { case Default: - buildFT3NewVacuumVessel(); // FT3 after Upgrade days March 2024 + buildFT3ScopingV3(); // v3 Dec 25 break; case Telescope: buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) @@ -729,9 +791,23 @@ void Detector::defineSensitiveVolumes() for (int direction : {0, 1}) { for (int iLayer = 0; iLayer < mNumberOfLayers; iLayer++) { volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); - v = geoManager->GetVolume(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)); - LOG(info) << "Adding FT3 Sensitive Volume => " << v->GetName(); - AddSensitiveVolume(v); + if (iLayer < 3) { // ML disks + v = geoManager->GetVolume(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)); + AddSensitiveVolume(v); + } else { // OT disks + for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { + std::string sensor_name_front = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_back = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + v = geoManager->GetVolume(sensor_name_front.c_str()); + if (v) { + AddSensitiveVolume(v); + } + v = geoManager->GetVolume(sensor_name_back.c_str()); + if (v) { + AddSensitiveVolume(v); + } + } + } } } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 01b512b996af2..97f42eca6143f 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -28,7 +28,10 @@ #include "TMathBase.h" // for Abs #include // for Sin, RadToDeg, DegToRad, Cos, Tan, etc +#include +#include #include // for snprintf +#include class TGeoMedium; @@ -40,6 +43,18 @@ ClassImp(FT3Layer); FT3Layer::~FT3Layer() = default; +TGeoMaterial* FT3Layer::carbonFiberMat = nullptr; +TGeoMedium* FT3Layer::medCarbonFiber = nullptr; + +TGeoMaterial* FT3Layer::kaptonMat = nullptr; +TGeoMedium* FT3Layer::kaptonMed = nullptr; + +TGeoMaterial* FT3Layer::waterMat = nullptr; +TGeoMedium* FT3Layer::waterMed = nullptr; + +TGeoMaterial* FT3Layer::foamMat = nullptr; +TGeoMedium* FT3Layer::medFoam = nullptr; + FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0) { // Creates a simple parametrized EndCap layer covering the given @@ -59,10 +74,157 @@ FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerNam LOG(info) << " Layer z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; } +void FT3Layer::initialize_mat() +{ + + if (carbonFiberMat) { + return; + } + + carbonFiberMat = new TGeoMaterial("CarbonFiber", 12.0, 6.0, 1.6); + medCarbonFiber = new TGeoMedium("CarbonFiber", 1, carbonFiberMat); + + auto* itsC = new TGeoElement("FT3_C", "Carbon", 6, 12.0107); + + auto* itsFoam = new TGeoMixture("FT3_Foam", 1); + itsFoam->AddElement(itsC, 1); + itsFoam->SetDensity(0.17); + + medFoam = new TGeoMedium("FT3_Foam", 1, itsFoam); + foamMat = medFoam->GetMaterial(); + + kaptonMat = new TGeoMaterial("Kapton (cooling pipe)", 13.84, 6.88, 1.346); + kaptonMed = new TGeoMedium("Kapton (cooling pipe)", 1, kaptonMat); + + waterMat = new TGeoMaterial("Water", 18.01528, 8.0, 1.064); + waterMed = new TGeoMedium("Water", 2, waterMat); +} + +static double y_circle(double x, double radius) +{ + return (x * x < radius * radius) ? std::sqrt(radius * radius - x * x) : 0; +} + +void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName) +{ + + FT3Layer::initialize_mat(); + + double carbonFiberThickness = 0.01; + double foamSpacingThickness = 0.5; + + TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); + + // volumes + TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, medCarbonFiber); + TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, medCarbonFiber); + + carbonFiberLayerVol1->SetLineColor(kGray + 2); + carbonFiberLayerVol2->SetLineColor(kGray + 2); + + double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; + + motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, mZ - zSeparation)); + motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, mZ + zSeparation)); + + double pipeOuterRadius = 0.20; + double kaptonThickness = 0.0025; + double pipeInnerRadius = pipeOuterRadius - kaptonThickness; + double pipeMaxLength = mOuterRadius * 2.0; + + int name_it = 0; + + // positions of the pipes depending on the overlap of the sensors inactive regions: (ALICE 3 dimensions) + // partial: + // std::vector X_pos = {-63.2, -58.4, -53.6, -48.8, -44.0, -39.199999999999996, -34.4, -29.599999999999994, -24.799999999999997, -19.999999999999993, -15.199999999999998, -10.399999999999993, -5.599999999999998, -0.7999999999999936, 4.000000000000002, 8.800000000000006, 13.600000000000001, 18.400000000000006, 23.200000000000003, 28.000000000000007, 32.800000000000004, 37.60000000000001, 42.400000000000006, 47.20000000000001, 52.00000000000001, 56.80000000000001, 61.60000000000001, 66.4}; + // complete: + // std::vector X_pos = {-63.4, -58.8, -54.199999999999996, -49.599999999999994, -44.99999999999999, -40.39999999999999, -35.79999999999999, -31.199999999999992, -26.59999999999999, -21.999999999999993, -17.39999999999999, -12.799999999999994, -8.199999999999992, -3.5999999999999934, 1.000000000000008, 5.600000000000007, 10.200000000000008, 14.800000000000008, 19.40000000000001, 24.000000000000007, 28.60000000000001, 33.20000000000001, 37.80000000000001, 42.40000000000001, 47.000000000000014, 51.600000000000016, 56.20000000000002, 60.80000000000002, 65.40000000000002}; + std::vector X_pos = {-62.3168, -57.9836, -53.650400000000005, -49.317200000000014, -44.984000000000016, -40.65080000000002, -36.31760000000002, -31.984400000000026, -27.65120000000003, -23.318000000000037, -18.98480000000004, -14.651600000000043, -10.318400000000047, -5.98520000000005, -1.6520000000000519, 2.6811999999999445, 7.014399999999941, 11.347599999999936, 15.680799999999934, 20.01399999999993, 24.347199999999926, 28.68039999999992, 33.013599999999926, 37.34679999999992, 41.980000000000004, 46.613200000000006, 51.246399999999994, 55.87960000000001, 60.5128}; + + for (double xPos : X_pos) { + + double pipeLength = pipeMaxLength; + double yMax = 0.0; + + TGeoRotation* rotation = new TGeoRotation(); + rotation->RotateX(90); + + if (std::abs(xPos) < mInnerRadius) { + double yInner = std::abs(y_circle(xPos, mInnerRadius)); + double yOuter = std::abs(y_circle(xPos, mOuterRadius)); + + yMax = 2 * yOuter; + pipeLength = yMax; + + double positiveYLength = yOuter - yInner; + + TGeoVolume* kaptonPipePos = new TGeoVolume((separationLayerName + "_KaptonPipePos_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), kaptonMed); + kaptonPipePos->SetLineColor(kGray); + TGeoVolume* waterVolumePos = new TGeoVolume((separationLayerName + "_WaterVolumePos_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), waterMed); + waterVolumePos->SetLineColor(kBlue); + + motherVolume->AddNode(waterVolumePos, 1, new TGeoCombiTrans(xPos, (yInner + yOuter) / 2.0, mZ, rotation)); + + TGeoVolume* kaptonPipeNeg = new TGeoVolume((separationLayerName + "_KaptonPipeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), kaptonMed); + kaptonPipeNeg->SetLineColor(kGray); + TGeoVolume* waterVolumeNeg = new TGeoVolume((separationLayerName + "_WaterVolumeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), waterMed); + waterVolumeNeg->SetLineColor(kBlue); + + motherVolume->AddNode(waterVolumeNeg, 1, new TGeoCombiTrans(xPos, -(yInner + yOuter) / 2.0, mZ, rotation)); + + motherVolume->AddNode(kaptonPipePos, 1, new TGeoCombiTrans(xPos, (yInner + yOuter) / 2.0, mZ, rotation)); + motherVolume->AddNode(kaptonPipeNeg, 1, new TGeoCombiTrans(xPos, -(yInner + yOuter) / 2.0, mZ, rotation)); + + } else { + + double yOuter = std::abs(y_circle(xPos, mOuterRadius)); + yMax = 2 * yOuter; + pipeLength = yMax; + + TGeoVolume* kaptonPipe = new TGeoVolume((separationLayerName + "_KaptonPipe_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, pipeLength / 2), kaptonMed); + kaptonPipe->SetLineColor(kGray); + TGeoVolume* waterVolume = new TGeoVolume((separationLayerName + "_WaterVolume_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, pipeLength / 2), waterMed); + waterVolume->SetLineColor(kBlue); + + motherVolume->AddNode(waterVolume, 1, new TGeoCombiTrans(xPos, 0, mZ, rotation)); + motherVolume->AddNode(kaptonPipe, 1, new TGeoCombiTrans(xPos, 0, mZ, rotation)); + } + + name_it++; + } +} + +void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName) +{ + + FT3Layer::initialize_mat(); + + double carbonFiberThickness = 0.01; + double foamSpacingThickness = 1.0; + + TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); + TGeoTube* foamLayer = new TGeoTube(mInnerRadius, mOuterRadius, foamSpacingThickness / 2); + + // volumes + TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, medCarbonFiber); + TGeoVolume* foamLayerVol = new TGeoVolume((separationLayerName + "_Foam").c_str(), foamLayer, medFoam); + TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, medCarbonFiber); + + carbonFiberLayerVol1->SetLineColor(kGray + 2); + foamLayerVol->SetLineColor(kBlack); + foamLayerVol->SetFillColorAlpha(kBlack, 1.0); + carbonFiberLayerVol2->SetLineColor(kGray + 2); + + double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; + + motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, mZ - zSeparation)); + motherVolume->AddNode(foamLayerVol, 1, new TGeoTranslation(0, 0, mZ)); + motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, mZ + zSeparation)); +} + void FT3Layer::createLayer(TGeoVolume* motherVolume) { - if (mLayerNumber >= 0) { - // Create tube, set sensitive volume, add to mother volume + if (mLayerNumber >= 0 && mLayerNumber < 3) { std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber), sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); @@ -93,6 +255,20 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); - return; + } else if (mLayerNumber >= 3) { + + FT3Module module; + + // layer structure + std::string frontLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Front"; + std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back"; + std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); + + // createSeparationLayer_waterCooling(motherVolume, separationLayerName); + createSeparationLayer(motherVolume, separationLayerName); + + // create disk faces + module.createModule(mZ, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "front", "rectangular", motherVolume); + module.createModule(mZ, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "back", "rectangular", motherVolume); } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx new file mode 100644 index 0000000000000..9318554837706 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -0,0 +1,706 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FT3Module.cxx +/// \brief Implementation of the FT3Module class + +#include "FT3Simulation/FT3Module.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TGeoMaterial* FT3Module::siliconMat = nullptr; +TGeoMedium* FT3Module::siliconMed = nullptr; + +TGeoMaterial* FT3Module::copperMat = nullptr; +TGeoMedium* FT3Module::copperMed = nullptr; + +TGeoMaterial* FT3Module::kaptonMat = nullptr; +TGeoMedium* FT3Module::kaptonMed = nullptr; + +TGeoMaterial* FT3Module::epoxyMat = nullptr; +TGeoMedium* FT3Module::epoxyMed = nullptr; + +TGeoMaterial* FT3Module::AluminumMat = nullptr; +TGeoMedium* FT3Module::AluminumMed = nullptr; + +void FT3Module::initialize_materials() +{ + LOG(debug) << "FT3Module: initialize_materials"; + if (siliconMat) { + return; + } + + TGeoManager* geoManager = gGeoManager; + + auto* itsH = new TGeoElement("FT3_H", "Hydrogen", 1, 1.00794); + auto* itsC = new TGeoElement("FT3_C", "Carbon", 6, 12.0107); + auto* itsO = new TGeoElement("FT3_O", "Oxygen", 8, 15.994); + + siliconMat = new TGeoMaterial("FT3_Silicon", 28.0855, 14, 2.33); + siliconMed = new TGeoMedium("FT3_Silicon", 1, siliconMat); + + copperMat = new TGeoMaterial("FT3_Copper", 63.546, 29, 8.96); + copperMed = new TGeoMedium("FT3_Copper", 2, copperMat); + + kaptonMat = new TGeoMaterial("FT3_Kapton", 13.84, 6.88, 1.346); + kaptonMed = new TGeoMedium("FT3_Kapton", 3, kaptonMat); + + // Epoxy: C18 H19 O3 + auto* itsEpoxy = new TGeoMixture("FT3_Epoxy", 3); + itsEpoxy->AddElement(itsC, 18); + itsEpoxy->AddElement(itsH, 19); + itsEpoxy->AddElement(itsO, 3); + itsEpoxy->SetDensity(2.186); + + epoxyMed = new TGeoMedium("FT3_Epoxy", 4, itsEpoxy); + epoxyMat = epoxyMed->GetMaterial(); + + AluminumMat = new TGeoMaterial("Aluminum", 26.98, 13, 2.7); + AluminumMed = new TGeoMedium("Aluminum", 5, AluminumMat); + LOG(debug) << "FT3Module: done initialize_materials"; +} + +double calculate_y_circle(double x, double radius) +{ + return (x * x < radius * radius) ? std::sqrt(radius * radius - x * x) : 0; +} + +void FT3Module::create_layout(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume) +{ + + LOG(debug) << "FT3Module: create_layout - Layer " << layerNumber << ", Direction " << direction << ", Face " << face; + TGeoManager* geoManager = gGeoManager; + + FT3Module::initialize_materials(); + + // double sensor_width = 2.5; + // double sensor_height = 9.6; + // double active_width = 2.3; + // double active_height = 9.6; + + double sensor_width = 5.0; + double sensor_height = 9.6; + double inactive_width = 0.2; // per side + double active_width = 4.6; + double active_height = 9.6; + + double silicon_thickness = 0.01; + double copper_thickness = 0.006; + double kapton_thickness = 0.03; + double epoxy_thickness = 0.0012; + + double carbonFiberThickness = 0.01; + + double foamSpacingThickness = 1.0; + + int dist_offset = 0; + + double x_offset; + double y_offset; + + double z_offset = (face == "front") ? -foamSpacingThickness / 2.0 - carbonFiberThickness : foamSpacingThickness / 2.0 + carbonFiberThickness; + + // offset correction + if (sensor_height == 3.2 && sensor_width == 2.5) { + x_offset = 0.8; + y_offset = 1.5; + } else if (sensor_height == 19.2 && sensor_width == 5) { + x_offset = 0.7; + y_offset = 9; + + } else { + x_offset = sensor_width / 2; + y_offset = sensor_height / 2; + } + + double x_condition_min = 0; + double x_condition_max = 0; + double offset_Rin_lower = 0; + double offset_Rin_upper = 0; + bool adjust_bottom_y_pos = false; + bool adjust_bottom_y_neg = false; + double x_adjust_bottom_y_pos = 0; + double bottom_y_pos_value = 0; + double bottom_y_neg_value = 0; + + if (Rin == 7 && sensor_height == 9.6 && sensor_width == 5) { + x_condition_min = -Rin - 2; + x_condition_max = Rin; + adjust_bottom_y_pos = true; + adjust_bottom_y_neg = true; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; + + dist_offset = 2; + + } else if (Rin == 5 && sensor_height == 9.6 && sensor_width == 5) { + x_condition_min = -Rin - 6; + x_condition_max = Rin; + adjust_bottom_y_pos = true; + adjust_bottom_y_neg = true; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; + } else if ((Rin == 5 || Rin == 7) && sensor_height == 19.2) { + x_condition_min = -Rin - 3; + x_condition_max = Rin - 0.2; + dist_offset = 2; + adjust_bottom_y_pos = false; + adjust_bottom_y_neg = false; + } else if (Rin == 5 && sensor_height == 3.2) { + x_condition_min = -(Rin + 2.6); + x_condition_max = Rin + 1.5; + adjust_bottom_y_pos = true; + adjust_bottom_y_neg = true; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; + } else if (Rin == 7 && sensor_height == 3.2) { + x_condition_min = -Rin - 1; + x_condition_max = Rin - 0.2; + adjust_bottom_y_pos = true; + adjust_bottom_y_neg = true; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; + } else if (Rin == 5 && sensor_height == 9.6 && sensor_width == 2.5) { + x_condition_min = -(Rin + 2.6); + x_condition_max = Rin; + adjust_bottom_y_pos = true; + adjust_bottom_y_neg = true; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; + } else if (Rin == 7 && sensor_height == 9.6 && sensor_width == 2.5) { + x_condition_min = -Rin - 2.6; + x_condition_max = Rin + 1; + dist_offset = 2; + adjust_bottom_y_pos = true; + adjust_bottom_y_neg = true; + x_adjust_bottom_y_pos = 5.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; + } else { + std::cout << "Different config - to determine offsets needed." << std::endl; + x_condition_min = -Rin; + x_condition_max = Rin; + adjust_bottom_y_pos = false; + adjust_bottom_y_neg = false; + } + + double Rin_offset = (sensor_height == 19.2) ? 1 : 0; + double Rout_offset = (sensor_height == 19.2) ? 1 : 0; + + offset_Rin_lower = Rin - Rin_offset; + offset_Rin_upper = Rout + Rout_offset; + + std::set> placed_sensors; + int sensor_count = 0; + + int placementCounter = 0; + bool justSkipped = false; + + std::vector X_positions; + std::vector justSkipped1; + + if (sensor_width == 2.5) { + // logic for placement - x positions with complete overlap + if (face == "front") { + X_positions = {-63.4, -60.9, -54.2, -51.7, -45.0, -42.5, -35.8, -33.3, -26.6, -24.1, -17.4, -14.9, + -8.2, -5.7, 1.0, 3.5, 10.2, 12.7, 19.4, 21.9, 28.6, 31.1, 37.8, 40.3, 47.0, 49.5, + 56.2, 58.7, 65.4}; + justSkipped1 = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}; + } else if (face == "back") { + X_positions = {-65.5, -58.8, -56.3, -49.6, -47.1, -40.4, -37.9, -31.2, -28.7, -22.0, -19.5, -12.8, + -10.3, -3.6, -1.1, 5.6, 8.1, 14.8, 17.3, 24.0, 26.5, 33.2, 35.7, 42.4, 44.9, + 51.6, 54.1, 60.8, 63.3}; + justSkipped1 = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; + } + } else { + // filling for sensors with 2x width, each row skipped + if (face == "front") { + X_positions = {-63.4, -54.2, -45, -35.8, -26.6, -17.4, -8.2, 1., 10.2, 19.4, 28.6, 37.8, 47., 56.2, 65.4}; + justSkipped1 = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + } else if (face == "back") { + X_positions = {-58.8, -49.6, -40.4, -31.2, -22, -12.8, -3.6, 5.6, 14.8, 24, 33.2, 42.4, 51.6, 60.8}; + justSkipped1 = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + } + } + + if (layout_type == "rectangular") { + + double x_start = -Rout; + double x_end = Rout; + + std::vector x_positions; + for (double x = x_start; x <= x_end; x += sensor_width) { + x_positions.push_back(x); + } + + int rowCounter = 0; + const int rowsToAlternate = 2; + + for (size_t i = 0; i < X_positions.size(); ++i) { + + double x = X_positions[i]; + bool justSkippedValue = justSkipped1[i]; + + std::vector y_positions_positive; + std::vector y_positions_negative; + + for (double y = -Rout - Rin_offset; y <= Rout + Rin_offset; y += sensor_height) { + std::vector> corners = { + {x, y}, + {x + sensor_width, y}, + {x, y + sensor_height}, + {x + sensor_width, y + sensor_height}}; + + bool within_bounds = std::all_of(corners.begin(), corners.end(), [&](const std::pair& corner) { + double cx = corner.first; + double cy = corner.second; + return (offset_Rin_lower <= std::sqrt(cx * cx + cy * cy) && std::sqrt(cx * cx + cy * cy) <= offset_Rin_upper); + }); + + if (within_bounds) { + if (y >= 0) { + y_positions_positive.push_back(y); + } else { + y_positions_negative.push_back(y); + } + } + } + + // adjust y positions near inner circle for positive y + if (x_condition_min <= x && x <= x_condition_max && !y_positions_positive.empty()) { + double first_y_pos = y_positions_positive.front(); + double last_y_pos = y_positions_positive.back() - sensor_height; + double top_y_pos = std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout)); + double bottom_y_pos = std::max(calculate_y_circle(x, Rin), calculate_y_circle(x + sensor_width, Rin)); + double top_distance_pos = top_y_pos - last_y_pos; + + if (adjust_bottom_y_pos && x > x_adjust_bottom_y_pos) { + bottom_y_pos = bottom_y_pos_value; + } + + double bottom_distance_pos = first_y_pos - bottom_y_pos; + + if (std::abs(top_distance_pos + bottom_distance_pos) >= sensor_height) { + for (auto& y : y_positions_positive) { + y -= bottom_distance_pos - 0.2; + } + y_positions_positive.push_back(y_positions_positive.back() + sensor_height); + } + } + + // adjust y positions near inner circle for negative y + if (x_condition_min <= x && x <= x_condition_max && !y_positions_negative.empty()) { + double first_y_neg = y_positions_negative.front(); + double last_y_neg = y_positions_negative.back() + sensor_height; + double top_y_neg = -std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout)); + double bottom_y_neg = -std::max(calculate_y_circle(x, Rin), calculate_y_circle(x + sensor_width, Rin)); + double top_distance_neg = -(top_y_neg - first_y_neg); + + if (adjust_bottom_y_neg && x > x_adjust_bottom_y_pos) { + bottom_y_neg = bottom_y_neg_value; + } + + double bottom_distance_neg = -(last_y_neg - bottom_y_neg); + + top_distance_neg = std::abs(top_distance_neg); + bottom_distance_neg = std::abs(bottom_distance_neg); + std::sort(y_positions_negative.begin(), y_positions_negative.end()); + + if (std::abs(top_distance_neg + bottom_distance_neg) >= sensor_height) { + if (sensor_height == 19.2) { + for (auto& y : y_positions_negative) { + y -= bottom_distance_neg; + } + } else { + for (auto& y : y_positions_negative) { + y += bottom_distance_neg - 0.2; + } + } + y_positions_negative.push_back(y_positions_negative.front() - sensor_height); + } + } + + // adjust positions for the rest of the disk + if ((x < x_condition_min || x > x_condition_max) && !y_positions_negative.empty() && !y_positions_positive.empty()) { + double first_y_neg = y_positions_negative.front(); + double last_y_pos = y_positions_positive.back() + sensor_height; + double top_y_pos = std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout)); + double bottom_y_pos = -top_y_pos; + + double top_distance_pos = std::abs(top_y_pos - last_y_pos); + double bottom_distance_pos = std::abs(first_y_neg - bottom_y_pos); + + if (top_distance_pos + bottom_distance_pos >= sensor_height) { + for (auto& y : y_positions_positive) { + y += top_distance_pos - 0.2; + } + for (auto& y : y_positions_negative) { + y += top_distance_pos - 0.2; + } + double new_y = y_positions_negative.front() - sensor_height; + + if (static_cast(new_y) > static_cast(bottom_y_pos)) { + y_positions_negative.push_back(new_y); + } + } + + // Make symmetric adjustments + std::sort(y_positions_negative.begin(), y_positions_negative.end()); + std::sort(y_positions_positive.begin(), y_positions_positive.end()); + + double first_y_pos = y_positions_negative.front(); + + last_y_pos = y_positions_positive.back() + sensor_height; + + top_y_pos = std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout)); + bottom_y_pos = -top_y_pos; + top_distance_pos = std::abs(top_y_pos - last_y_pos); + bottom_distance_pos = std::abs(first_y_pos - bottom_y_pos); + + double Lb = (bottom_distance_pos + top_distance_pos) / 2; + + if (top_distance_pos < Lb) { + double shift = Lb - top_distance_pos; + for (auto& y : y_positions_negative) { + y -= shift; + } + for (auto& y : y_positions_positive) { + y -= shift; + } + } else if (top_distance_pos > Lb) { + double shift = top_distance_pos - Lb; + for (auto& y : y_positions_negative) { + y += shift; + } + for (auto& y : y_positions_positive) { + y += shift; + } + } + } + + std::vector y_positions = y_positions_positive; + y_positions.insert(y_positions.end(), y_positions_negative.begin(), y_positions_negative.end()); + + for (double y : y_positions) { + + int SiColor; + double R_material_threshold = 0; + + if (placed_sensors.find({x, y}) == placed_sensors.end()) { + placed_sensors.insert({x, y}); + TGeoVolume* sensor; + + double inactive_width = (sensor_width - active_width) / 2; + double left_inactive_x_shift; + double right_inactive_x_shift; + double active_x_shift_sensor; + + if (face == "front") { + + double active_x_shift, inactive_x_shift; + + if (justSkippedValue) { + active_x_shift = x + inactive_width / 2; + active_x_shift_sensor = active_x_shift + inactive_width; + + inactive_x_shift = x - active_width / 2 + inactive_width / 2; + } else { + active_x_shift = x - inactive_width / 2; + active_x_shift_sensor = active_x_shift - inactive_width; + + inactive_x_shift = x + active_width / 2 - inactive_width / 2; + } + + double inactive_x_shift_left, inactive_x_shift_right; + + if (sensor_width == 5.0) { + + inactive_x_shift_left = x - sensor_width / 2 + inactive_width; + inactive_x_shift_right = x + sensor_width / 2; + } + + std::vector> corners_shifted = { + {x, y}, + {x + sensor_width, y}, + {x, y + sensor_height}, + {x + sensor_width, y + sensor_height}}; + + bool within_bounds = true; + for (const auto& corner : corners_shifted) { + double cx = corner.first; + double cy = corner.second; + double dist = std::sqrt(cx * cx + cy * cy); + + if (Rin > dist || dist >= Rout) { + within_bounds = false; + break; + } + } + + if (within_bounds) { + + double r_squared = (x + x_offset) * (x + x_offset) + (y + y_offset) * (y + y_offset); + + if (r_squared < R_material_threshold * R_material_threshold) { + silicon_thickness = 0.005; + copper_thickness = 0.00475; + kapton_thickness = 0.03; + epoxy_thickness = 0.0012; + + SiColor = kOrange; + } else { + silicon_thickness = 0.01; + copper_thickness = 0.006; + kapton_thickness = 0.03; + epoxy_thickness = 0.0012; + + SiColor = kGreen; + } + + if (sensor_width == 2.5) { + // silicon + std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); + sensor->SetLineColor(SiColor); + sensor->SetFillColorAlpha(SiColor, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift_sensor + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2)); + + std::string inactive_name = "FT3inactive_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(inactive_name.c_str(), siliconMed, (sensor_width - active_width) / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(kRed); + sensor->SetFillColorAlpha(kRed, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2)); + + } else { + + std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(SiColor); + sensor->SetFillColorAlpha(SiColor, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + x + inactive_width / 2, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2)); + + std::string inactive_name_left = "FT3inactive_left_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(inactive_name_left.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(kRed); + sensor->SetFillColorAlpha(kRed, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_left, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2)); + + std::string inactive_name_right = "FT3inactive_right_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(inactive_name_right.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(kRed); + sensor->SetFillColorAlpha(kRed, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_right, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2)); + } + + // silicon-to-FPC epoxy glue + std::string glue_up_name = "FT3glue_up_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(glue_up_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + sensor->SetLineColor(kBlue); + sensor->SetFillColorAlpha(kBlue, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness / 2)); + + if (r_squared < R_material_threshold * R_material_threshold) { + std::string alu_name = "FT3aluminum_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(alu_name.c_str(), AluminumMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + sensor->SetLineColor(kBlack); + sensor->SetFillColorAlpha(kBlack, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness / 2)); + + } else { + std::string copper_name = "FT3copper_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(copper_name.c_str(), copperMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + sensor->SetLineColor(kBlack); + sensor->SetFillColorAlpha(kBlack, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness / 2)); + } + + // kapton + std::string fpc_name = "FT3fpc_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(fpc_name.c_str(), kaptonMed, sensor_width / 2, sensor_height / 2, kapton_thickness / 2); + sensor->SetLineColor(kGreen); + sensor->SetFillColorAlpha(kGreen, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness / 2)); + + // FPC-to-support epoxy glue + std::string glue_down_name = "FT3glue_down_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(glue_down_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + sensor->SetLineColor(kBlue); + sensor->SetFillColorAlpha(kBlue, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset - epoxy_thickness / 2)); + } + } else { + double x_shifted = x; + double inactive_x_shift, active_x_shift; + double active_x_shift_sensor; + + if (justSkippedValue) { + active_x_shift = x + inactive_width / 2; + active_x_shift_sensor = active_x_shift + inactive_width; + + inactive_x_shift = x - active_width / 2 + inactive_width / 2; + } else { + active_x_shift = x - inactive_width / 2; + active_x_shift_sensor = active_x_shift - inactive_width; + + inactive_x_shift = x + active_width / 2 - inactive_width / 2; + } + + double inactive_x_shift_left, inactive_x_shift_right; + + if (sensor_width == 5.0) { + + inactive_x_shift_left = x - sensor_width / 2 + inactive_width; + inactive_x_shift_right = x + sensor_width / 2; + } + + std::vector> corners_shifted = { + {x_shifted, y}, + {x_shifted + sensor_width, y}, + {x_shifted, y + sensor_height}, + {x_shifted + sensor_width, y + sensor_height}}; + + bool within_bounds = true; + for (const auto& corner : corners_shifted) { + double cx = corner.first; + double cy = corner.second; + double dist = std::sqrt(cx * cx + cy * cy); + + if (Rin > dist + dist_offset || dist >= Rout) { + within_bounds = false; + break; + } + } + + if (within_bounds) { + + double r_squared = (x + x_offset) * (x + x_offset) + (y + y_offset) * (y + y_offset); + + if (r_squared < R_material_threshold * R_material_threshold) { + silicon_thickness = 0.005; + copper_thickness = 0.00475; // thinner -> + replaced by alu + kapton_thickness = 0.03; + epoxy_thickness = 0.0006; + + SiColor = kOrange; + } else { + silicon_thickness = 0.01; + copper_thickness = 0.006; + kapton_thickness = 0.03; + epoxy_thickness = 0.0012; + + SiColor = kGreen; + } + + // FPC-to-support epoxy glue + std::string glue_down_name = "FT3glue_down_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(glue_down_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + sensor->SetLineColor(kBlue); + sensor->SetFillColorAlpha(kBlue, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset + epoxy_thickness / 2)); + + // Kapton + std::string fpc_name = "FT3fpc_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(fpc_name.c_str(), kaptonMed, sensor_width / 2, sensor_height / 2, kapton_thickness / 2); + sensor->SetLineColor(kGreen); + sensor->SetFillColorAlpha(kGreen, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness / 2)); + + if (r_squared < R_material_threshold * R_material_threshold) { + // replace copper with alu + std::string alu_name = "FT3aluminum_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(alu_name.c_str(), AluminumMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + sensor->SetLineColor(kBlack); + sensor->SetFillColorAlpha(kBlack, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness / 2)); + + } else { + std::string copper_name = "FT3copper_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(copper_name.c_str(), copperMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + sensor->SetLineColor(kBlack); + sensor->SetFillColorAlpha(kBlack, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness / 2)); + } + + // silicon-to-FPC epoxy glue + std::string glue_up_name = "FT3glue_up_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(glue_up_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + sensor->SetLineColor(kBlue); + sensor->SetFillColorAlpha(kBlue, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness / 2)); + + if (sensor_width == 2.5) { + + std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); + sensor->SetLineColor(SiColor); + sensor->SetFillColorAlpha(SiColor, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift_sensor + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2)); + + std::string inactive_name = "FT3inactive_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(inactive_name.c_str(), siliconMed, (sensor_width - active_width) / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(kRed); + sensor->SetFillColorAlpha(kRed, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2)); + + } else { + // active (4.6 cm centered) + std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(SiColor); + sensor->SetFillColorAlpha(SiColor, 0.4); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + x_shifted + inactive_width / 2, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2)); + + // left inactive strip + std::string inactive_name_left = "FT3inactive_left_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(inactive_name_left.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(kRed); + sensor->SetFillColorAlpha(kRed, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_left, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2)); + + // right inactive strip + std::string inactive_name_right = "FT3inactive_right_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + sensor = geoManager->MakeBox(inactive_name_right.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + sensor->SetLineColor(kRed); + sensor->SetFillColorAlpha(kRed, 1.0); + motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_right, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2)); + } + } + } + } + } + + rowCounter++; + } + } + LOG(debug) << "FT3Module: done create_layout"; +} + +void FT3Module::createModule(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume) +{ + + LOG(debug) << "FT3Module: createModule - Layer " << layerNumber << ", Direction " << direction << ", Face " << face; + create_layout(mZ, layerNumber, direction, Rin, Rout, overlap, face, layout_type, motherVolume); + LOG(debug) << "FT3Module: done createModule"; +} diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 10d8c5ced94dd..b74fc6d6869dd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -25,6 +25,9 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper bool enableOuterTOF = true; bool enableForwardTOF = true; bool enableBackwardTOF = true; + std::string detectorPattern = ""; + bool segmentedInnerTOF = false; // If the inner TOF layer is segmented + bool segmentedOuterTOF = false; // If the outer TOF layer is segmented O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h index 1f3b2f4fe9fac..f3c4e3ddd6276 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h @@ -60,7 +60,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index b7cc0a05c1c2e..df3687b2b2ea4 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -14,6 +14,8 @@ #include #include +#include +#include namespace o2 { @@ -23,7 +25,8 @@ class Layer { public: Layer() = default; - Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, bool isBarrel = true); + Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, + int layout = kBarrel, int nSegments = 0, float segmentSize = 0.0, int nSensorsPerSegment = 0, double tiltAngle = 0.0); ~Layer() = default; auto getInnerRadius() const { return mInnerRadius; } @@ -33,9 +36,14 @@ class Layer auto getx2X0() const { return mX2X0; } auto getChipThickness() const { return mChipThickness; } auto getName() const { return mLayerName; } - auto getIsBarrel() const { return mIsBarrel; } + auto getLayout() const { return mLayout; } + auto getSegments() const { return mSegments; } + static constexpr int kBarrel = 0; + static constexpr int kDisk = 1; + static constexpr int kBarrelSegmented = 2; + static constexpr int kDiskSegmented = 3; - virtual void createLayer(TGeoVolume* motherVolume){}; + virtual void createLayer(TGeoVolume* motherVolume) {}; protected: std::string mLayerName; @@ -45,7 +53,11 @@ class Layer float mZOffset{0.f}; // Of use when fwd layers float mX2X0; float mChipThickness; - bool mIsBarrel{true}; + int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) + // To be used only in case of the segmented layout, to define the number of segments in phi (for barrel) or in r (for disk) + std::pair mSegments{0, 0.0f}; // Number and size of segments in phi (for barrel) or in r (for disk) in case of segmented layout + int mSensorsPerSegment{0}; // Number of sensors along a segment + double mTiltAngle{0.0}; // Tilt angle in degrees to be applied as a rotation around the local center of the segment }; class ITOFLayer : public Layer @@ -53,6 +65,7 @@ class ITOFLayer : public Layer public: using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; + static std::vector mRegister; }; class OTOFLayer : public Layer @@ -60,6 +73,7 @@ class OTOFLayer : public Layer public: using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; + static std::vector mRegister; }; class FTOFLayer : public Layer diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index a2bba7cc5fe35..0742af3a1340a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -20,8 +20,6 @@ #include "IOTOFSimulation/Detector.h" #include "IOTOFBase/IOTOFBaseParam.h" -using o2::itsmft::Hit; - namespace o2 { namespace iotof @@ -40,7 +38,10 @@ Detector::Detector(bool active) mHits(o2::utils::createSimVector()) { auto& iotofPars = IOTOFBaseParam::Instance(); - configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF); + configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, + iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, + iotofPars.detectorPattern, + iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF); } Detector::~Detector() @@ -56,19 +57,63 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented) { - if (itof) { - mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, 19.f, 0.f, 124.f, 0.f, 0.02f, true); // iTOF + + float radiusInnerTof = 19.f; + float radiusOuterTof = 85.f; + float lengthInnerTof = 124.f; + float lengthOuterTof = 680.f; + std::pair radiusRangeDiskTof = {15.f, 100.f}; + float zForwardTof = 370.f; + LOG(info) << "Configuring IOTOF layers with '" << pattern << "' pattern"; + if (pattern == "") { + LOG(info) << "Default pattern"; + } else if (pattern == "v3b") { + ftof = false; + btof = false; + } else if (pattern == "v3b1a") { + lengthOuterTof = 500.f; + zForwardTof = 270.f; + radiusRangeDiskTof = {30.f, 100.f}; + } else if (pattern == "v3b1b") { + lengthOuterTof = 500.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else if (pattern == "v3b2a") { + lengthOuterTof = 440.f; + zForwardTof = 270.f; + radiusRangeDiskTof = {30.f, 120.f}; + } else if (pattern == "v3b2b") { + lengthOuterTof = 440.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else if (pattern == "v3b3") { + lengthOuterTof = 580.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else { + LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; } - if (otof) { - mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, 85.f, 0.f, 680.f, 0.f, 0.02f, true); // oTOF + if (itof) { // iTOF + mITOFLayer = itofSegmented ? ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrelSegmented, + 24, 5.42, 80, 10) + : ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrel); + } + if (otof) { // oTOF + mOTOFLayer = otofSegmented ? OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrelSegmented, + 62, 9.74, 432, 5) + : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrel); } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, 15.f, 100.f, 0.f, 370.f, 0.02f, false); // fTOF + mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, FTOFLayer::kDisk); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, 15.f, 100.f, 0.f, -370.f, 0.02f, false); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, BTOFLayer::kDisk); // bTOF } } @@ -151,14 +196,18 @@ void Detector::defineSensitiveVolumes() // The names of the IOTOF sensitive volumes have the format: IOTOFLayer(0...mLayers.size()-1) auto& iotofPars = IOTOFBaseParam::Instance(); if (iotofPars.enableInnerTOF) { - v = geoManager->GetVolume(GeometryTGeo::getITOFSensorPattern()); - LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (const std::string& itofSensor : ITOFLayer::mRegister) { + v = geoManager->GetVolume(itofSensor.c_str()); + LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } if (iotofPars.enableOuterTOF) { - v = geoManager->GetVolume(GeometryTGeo::getOTOFSensorPattern()); - LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (const std::string& otofSensor : OTOFLayer::mRegister) { + v = geoManager->GetVolume(otofSensor.c_str()); + LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } if (iotofPars.enableForwardTOF) { v = geoManager->GetVolume(GeometryTGeo::getFTOFSensorPattern()); @@ -214,28 +263,28 @@ bool Detector::ProcessHits(FairVolume* vol) bool startHit = false, stopHit = false; unsigned char status = 0; if (fMC->IsTrackEntering()) { - status |= Hit::kTrackEntering; + status |= o2::itsmft::Hit::kTrackEntering; } if (fMC->IsTrackInside()) { - status |= Hit::kTrackInside; + status |= o2::itsmft::Hit::kTrackInside; } if (fMC->IsTrackExiting()) { - status |= Hit::kTrackExiting; + status |= o2::itsmft::Hit::kTrackExiting; } if (fMC->IsTrackOut()) { - status |= Hit::kTrackOut; + status |= o2::itsmft::Hit::kTrackOut; } if (fMC->IsTrackStop()) { - status |= Hit::kTrackStopped; + status |= o2::itsmft::Hit::kTrackStopped; } if (fMC->IsTrackAlive()) { - status |= Hit::kTrackAlive; + status |= o2::itsmft::Hit::kTrackAlive; } // track is entering or created in the volume - if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) { + if ((status & o2::itsmft::Hit::kTrackEntering) || (status & o2::itsmft::Hit::kTrackInside && !mTrackData.mHitStarted)) { startHit = true; - } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) { + } else if ((status & (o2::itsmft::Hit::kTrackExiting | o2::itsmft::Hit::kTrackOut | o2::itsmft::Hit::kTrackStopped))) { stopHit = true; } @@ -264,9 +313,9 @@ bool Detector::ProcessHits(FairVolume* vol) fMC->CurrentVolOffID(3, halfstave); fMC->CurrentVolOffID(4, stave); - Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), - mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), - mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); + o2::itsmft::Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), + mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); // RS: not sure this is needed // Increment number of Detector det points in TParticle diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 53c07d1fa4978..169a1271da47e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -14,84 +14,225 @@ #include "Framework/Logger.h" +#include +#include #include #include +#include + +#include +#include namespace o2 { namespace iotof { -Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, bool isBarrel) - : mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mZLength(zLength), mZOffset(zOffset), mX2X0(layerX2X0), mIsBarrel(isBarrel) +Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, int layout, int nSegments, float segmentSize, int nSensorsPerSegment, double tiltAngle) + : mLayerName(layerName), + mInnerRadius(rInn), + mOuterRadius(rOut), + mZLength(zLength), + mZOffset(zOffset), + mX2X0(layerX2X0), + mLayout(layout), + mSegments(nSegments, segmentSize), + mSensorsPerSegment(nSensorsPerSegment), + mTiltAngle(tiltAngle) { float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; - if (isBarrel) { - mOuterRadius = mInnerRadius + mChipThickness; - } else { - mZLength = mChipThickness; + std::string name = ""; + switch (layout) { + case kBarrel: + case kBarrelSegmented: + name = "barrel"; + mOuterRadius = mInnerRadius + mChipThickness; + break; + case kDisk: + case kDiskSegmented: + name = "forward"; + mZLength = mChipThickness; + break; + default: + LOG(fatal) << "Invalid layout " << layout; + } + if (1) { // Sanity checks + if (mInnerRadius > mOuterRadius) { + LOG(fatal) << "Invalid layer dimensions: rInner " << mInnerRadius << " cm is larger than rOuter " << mOuterRadius << " cm"; + } + if ((mSegments.first != 0 || mSegments.second != 0.0f) && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mSegments.first << " is set for non-segmented layout " << layout; + } + if ((mSegments.first <= 1 || mSegments.second <= 0.0f) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mSegments.first << " must be positive for segmented layout " << layout; + } + if (mSensorsPerSegment <= 0 && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of sensors per segment " << mSensorsPerSegment << " must be positive for segmented layout " << layout; + } + if (std::abs(mTiltAngle) > 0.1 && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is set for non-segmented layout " << layout; + } } - LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", isBarrel ? std::string("barrel") : std::string("forward"), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); + + LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", name.c_str(), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); } +std::vector ITOFLayer::mRegister; void ITOFLayer::createLayer(TGeoVolume* motherVolume) { - std::string chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(), - sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); - - TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + const std::string chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(); + const std::string sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); - LOGP(info, "Media: {} {}", (void*)medSi, (void*)medAir); - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); - layerVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, nullptr); + switch (mLayout) { + case kBarrel: { + TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + + TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + layerVol->SetLineColor(kRed + 3); + + LOGP(info, "Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + LOGP(info, "Inserting Barrel {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + case kBarrelSegmented: { + const double circumference = TMath::TwoPi() * 0.5 * (mInnerRadius + mOuterRadius); + const double segmentSize = mSegments.second; // cm circumference / mSegments; + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kRed + 3); + + for (int i = 0; i < mSegments.first; ++i) { + LOGP(info, "iTOF: Creating segment {}/{} with size {} and thickness {}cm", i + 1, mSegments.first, segmentSize, (mOuterRadius - mInnerRadius)); + const double hx = 0.5 * segmentSize; + const double hy = 0.5 * (mOuterRadius - mInnerRadius); + const double hz = 0.5 * mZLength; + TGeoBBox* sensor = new TGeoBBox(hy, hx, hz); + TGeoBBox* chip = new TGeoBBox(hy, hx, hz); + const std::string segmentTag = Form("segment%d", i + 1); + TGeoVolume* sensVol = new TGeoVolume(Form("%s_%s", sensName.c_str(), segmentTag.c_str()), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(Form("%s_%s", chipName.c_str(), segmentTag.c_str()), chip, medSi); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + + LOGP(info, " Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + const double phi = TMath::TwoPi() * i / mSegments.first; + + LOG(info) << " Tilting angle for segment " << i + 1 << ": " << phi * TMath::RadToDeg() << " degrees"; + const double x = avgRadius * TMath::Cos(phi); + const double y = avgRadius * TMath::Sin(phi); + auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + mTiltAngle, 0, 0); + auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1 + i, transformation); + } + LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + default: + LOG(fatal) << "Invalid layout " << mLayout; + } } +std::vector OTOFLayer::mRegister; void OTOFLayer::createLayer(TGeoVolume* motherVolume) { std::string chipName = o2::iotof::GeometryTGeo::getOTOFChipPattern(), sensName = o2::iotof::GeometryTGeo::getOTOFSensorPattern(); - TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); + LOGP(info, "Media: {} {}", (void*)medSi, (void*)medAir); - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); - layerVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, nullptr); + switch (mLayout) { + case kBarrel: { + TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + + TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + layerVol->SetLineColor(kRed + 3); + + LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + case kBarrelSegmented: { + const double circumference = TMath::TwoPi() * 0.5 * (mInnerRadius + mOuterRadius); + const double segmentSize = mSegments.second; // cm circumference / mSegments; + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kRed + 3); + + for (int i = 0; i < mSegments.first; ++i) { + LOGP(info, "oTOF: Creating segment {}/{} with size {} and thickness {}cm", i + 1, mSegments.first, segmentSize, (mOuterRadius - mInnerRadius)); + const double hx = 0.5 * segmentSize; + const double hy = 0.5 * (mOuterRadius - mInnerRadius); + const double hz = 0.5 * mZLength; + TGeoBBox* sensor = new TGeoBBox(hy, hx, hz); + TGeoBBox* chip = new TGeoBBox(hy, hx, hz); + const std::string segmentTag = Form("segment%d", i + 1); + TGeoVolume* sensVol = new TGeoVolume(Form("%s_%s", sensName.c_str(), segmentTag.c_str()), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(Form("%s_%s", chipName.c_str(), segmentTag.c_str()), chip, medSi); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + + LOGP(info, " Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + const double phi = TMath::TwoPi() * i / mSegments.first; + + LOG(info) << " Tilting angle for segment " << i + 1 << ": " << phi * TMath::RadToDeg() << " degrees"; + const double x = avgRadius * TMath::Cos(phi); + const double y = avgRadius * TMath::Sin(phi); + auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + mTiltAngle, 0, 0); + auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1 + i, transformation); + } + LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + default: + LOG(fatal) << "Invalid layout " << mLayout; + } } void FTOFLayer::createLayer(TGeoVolume* motherVolume) diff --git a/Detectors/Upgrades/ALICE3/MID/base/include/MI3Base/MI3BaseParam.h b/Detectors/Upgrades/ALICE3/MID/base/include/MI3Base/MI3BaseParam.h index 7061a05bfeb37..913e27e85c207 100644 --- a/Detectors/Upgrades/ALICE3/MID/base/include/MI3Base/MI3BaseParam.h +++ b/Detectors/Upgrades/ALICE3/MID/base/include/MI3Base/MI3BaseParam.h @@ -19,7 +19,19 @@ namespace o2 { namespace mi3 { + +// ** +// ** Parameters for MID base configuration +// ** + +enum MIDLayout : int { + StandardRadius = 0, + ReducedRadius = 1 +}; + struct MIDBaseParam : public o2::conf::ConfigurableParamHelper { + int mLayout = MIDLayout::StandardRadius; + O2ParamDef(MIDBaseParam, "MIDBase"); }; diff --git a/Detectors/Upgrades/ALICE3/MID/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/MID/simulation/src/Detector.cxx index 36f5b376563c0..0eaf401e40596 100644 --- a/Detectors/Upgrades/ALICE3/MID/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/MID/simulation/src/Detector.cxx @@ -126,8 +126,16 @@ void Detector::createGeometry() // Build the MID mLayers.resize(2); - mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 301.f, 500.f); - mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 311.f, 520.f); // arbitrarily reduced to get multiple of 5.2f + auto& midParam = MIDBaseParam::Instance(); + const bool standardRadius = (midParam.mLayout == o2::mi3::MIDLayout::StandardRadius); + + if (standardRadius) { + mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 301.f, 500.f); + mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 311.f, 520.f); // arbitrarily reduced to get multiple of 5.2f + } else { + mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 266.f, 500.f); + mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 276.f, 520.f); + } for (auto& layer : mLayers) { layer.createLayer(vMID); diff --git a/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/PassiveBaseParam.h b/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/PassiveBaseParam.h index 3ac53c1bfb92b..671f436aabe7b 100644 --- a/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/PassiveBaseParam.h +++ b/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/PassiveBaseParam.h @@ -29,10 +29,16 @@ enum MagnetLayout : int { CopperStabilizer = 1 }; +enum DetLayout : int { + StandardRadius = 0, + ReducedRadius = 1 +}; + struct Alice3PassiveBaseParam : public o2::conf::ConfigurableParamHelper { // Geometry Builder parameters int mLayout = MagnetLayout::AluminiumStabilizer; + int mDetLayout = DetLayout::StandardRadius; O2ParamDef(Alice3PassiveBaseParam, "Alice3PassiveBase"); }; diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx index 7ce753db89536..924d977247c89 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx +++ b/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include // for TGeoTrap #include #include @@ -130,25 +131,52 @@ void Alice3Absorber::ConstructGeometry() } TGeoPcon* absorings = new TGeoPcon(0., 360., 18); - - absorings->DefineSection(0, 500, 236, 274); - absorings->DefineSection(1, 400, 236, 274); - absorings->DefineSection(2, 400, 232.5, 277.5); - absorings->DefineSection(3, 300, 232.5, 277.5); - absorings->DefineSection(4, 300, 227.5, 282.5); - absorings->DefineSection(5, 200, 227.5, 282.5); - absorings->DefineSection(6, 200, 222.5, 287.5); - absorings->DefineSection(7, 100, 222.5, 287.5); - absorings->DefineSection(8, 100, 220, 290); - absorings->DefineSection(9, -100, 220, 290); - absorings->DefineSection(10, -100, 222.5, 287.5); - absorings->DefineSection(11, -200, 222.5, 287.5); - absorings->DefineSection(12, -200, 227.5, 282.5); - absorings->DefineSection(13, -300, 227.5, 282.5); - absorings->DefineSection(14, -300, 232.5, 277.5); - absorings->DefineSection(15, -400, 232.5, 277.5); - absorings->DefineSection(16, -400, 236, 274); - absorings->DefineSection(17, -500, 236, 274); + auto& passiveBaseParam = Alice3PassiveBaseParam::Instance(); + switch (passiveBaseParam.mDetLayout) { + case o2::passive::DetLayout::StandardRadius: + absorings->DefineSection(0, 500, 236, 274); + absorings->DefineSection(1, 400, 236, 274); + absorings->DefineSection(2, 400, 232.5, 277.5); + absorings->DefineSection(3, 300, 232.5, 277.5); + absorings->DefineSection(4, 300, 227.5, 282.5); + absorings->DefineSection(5, 200, 227.5, 282.5); + absorings->DefineSection(6, 200, 222.5, 287.5); + absorings->DefineSection(7, 100, 222.5, 287.5); + absorings->DefineSection(8, 100, 220, 290); + absorings->DefineSection(9, -100, 220, 290); + absorings->DefineSection(10, -100, 222.5, 287.5); + absorings->DefineSection(11, -200, 222.5, 287.5); + absorings->DefineSection(12, -200, 227.5, 282.5); + absorings->DefineSection(13, -300, 227.5, 282.5); + absorings->DefineSection(14, -300, 232.5, 277.5); + absorings->DefineSection(15, -400, 232.5, 277.5); + absorings->DefineSection(16, -400, 236, 274); + absorings->DefineSection(17, -500, 236, 274); + break; + case o2::passive::DetLayout::ReducedRadius: + absorings->DefineSection(0, 500, 201, 239); + absorings->DefineSection(1, 400, 201, 239); + absorings->DefineSection(2, 400, 197.5, 242.5); + absorings->DefineSection(3, 300, 197.5, 242.5); + absorings->DefineSection(4, 300, 192.5, 247.5); + absorings->DefineSection(5, 200, 192.5, 247.5); + absorings->DefineSection(6, 200, 187.5, 252.5); + absorings->DefineSection(7, 100, 187.5, 252.5); + absorings->DefineSection(8, 100, 185, 255); + absorings->DefineSection(9, -100, 185, 255); + absorings->DefineSection(10, -100, 187.5, 252.5); + absorings->DefineSection(11, -200, 187.5, 252.5); + absorings->DefineSection(12, -200, 192.5, 247.5); + absorings->DefineSection(13, -300, 192.5, 247.5); + absorings->DefineSection(14, -300, 197.5, 242.5); + absorings->DefineSection(15, -400, 197.5, 242.5); + absorings->DefineSection(16, -400, 201, 239); + absorings->DefineSection(17, -500, 201, 239); + break; + default: + LOG(fatal) << "Unknown detector layout " << passiveBaseParam.mDetLayout; + break; + } // Insert absorings->SetName("absorings"); diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Magnet.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Magnet.cxx index 5c94c3e31a244..e6c1171829bfc 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/Magnet.cxx +++ b/Detectors/Upgrades/ALICE3/Passive/src/Magnet.cxx @@ -106,11 +106,41 @@ void Alice3Magnet::ConstructGeometry() // Passive Base configuration parameters auto& passiveBaseParam = Alice3PassiveBaseParam::Instance(); - const bool doCopperStabilizer = (passiveBaseParam.mLayout == o2::passive::MagnetLayout::CopperStabilizer); - if (doCopperStabilizer) { - mRestMaterialThickness -= 3.3; // cm Remove the Aluminium stabiliser - mRestMaterialThickness += 2.2; // cm Add the Copper stabiliser - LOG(debug) << "Alice 3 magnet: using Copper Stabilizer with thickness " << mRestMaterialThickness << " cm"; + + switch (passiveBaseParam.mDetLayout) { + case o2::passive::DetLayout::StandardRadius: + // Defined in the header file + break; + case o2::passive::DetLayout::ReducedRadius: + mInnerWrapInnerRadius = 125.f; // cm + mInnerWrapThickness = 1.f; // cm + mCoilInnerRadius = 145.f; // cm + mCoilThickness = 0.3f; // cm + mRestMaterialRadius = 145.3f; // cm + mRestMaterialThickness = 6.8f; // cm + mOuterWrapInnerRadius = 165.f; // cm + mOuterWrapThickness = 3.f; // cm + mZLength = 800.f; // cm + break; + default: + LOG(fatal) << "Unknown detector layout " << passiveBaseParam.mDetLayout; + break; + } + + bool doCopperStabilizer = false; + switch (passiveBaseParam.mLayout) { + case o2::passive::MagnetLayout::AluminiumStabilizer: + // Handled in the header file + break; + case o2::passive::MagnetLayout::CopperStabilizer: + doCopperStabilizer = true; + mRestMaterialThickness -= 3.3; // cm Remove the Aluminium stabiliser + mRestMaterialThickness += 2.2; // cm Add the Copper stabiliser + LOG(debug) << "Alice 3 magnet: using Copper Stabilizer with thickness " << mRestMaterialThickness << " cm"; + break; + default: + LOG(fatal) << "Unknown magnet layout " << passiveBaseParam.mLayout; + break; } TGeoManager* geoManager = gGeoManager; diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx index 7dfd26a79b38d..fe0a1c50330fe 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx +++ b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx @@ -122,7 +122,8 @@ void Alice3Pipe::ConstructGeometry() // Add everything to the barrel barrel->AddNode(pipeVolume, 1, new TGeoTranslation(0, 30.f, 0)); - pipeVolume->SetLineColor(kGreen + 3); + pipeVolume->SetLineColor(37); + pipeVolume->SetTransparency(0); } void Alice3Pipe::createMaterials() diff --git a/Detectors/Upgrades/ALICE3/RICH/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/RICH/base/src/GeometryTGeo.cxx index ebe6dcfbdc79a..01d242d6c64cd 100644 --- a/Detectors/Upgrades/ALICE3/RICH/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/RICH/base/src/GeometryTGeo.cxx @@ -21,7 +21,7 @@ std::unique_ptr GeometryTGeo::sInstance; std::string GeometryTGeo::sVolumeName = "RICHV"; std::string GeometryTGeo::sRingName = "RICHRing"; std::string GeometryTGeo::sChipName = "RICHChip"; -std::string GeometryTGeo::sSensorName = "RICHSensor"; +std::string GeometryTGeo::sSensorName = "RICHPhotoTile"; std::string GeometryTGeo::sSensorFWDName = "FWDRICHSensor"; // only one big sensor for now std::string GeometryTGeo::sSensorBWDName = "BWDRICHSensor"; // only one big sensor for now diff --git a/Detectors/Upgrades/ALICE3/RICH/simulation/include/RICHSimulation/RICHRing.h b/Detectors/Upgrades/ALICE3/RICH/simulation/include/RICHSimulation/RICHRing.h index a7892c210e310..296e24cbd8f06 100644 --- a/Detectors/Upgrades/ALICE3/RICH/simulation/include/RICHSimulation/RICHRing.h +++ b/Detectors/Upgrades/ALICE3/RICH/simulation/include/RICHSimulation/RICHRing.h @@ -54,6 +54,8 @@ class Ring auto getDeltaPhiPos() const { return TMath::TwoPi() / mNTiles; } void createRing(TGeoVolume* motherVolume); + int getPosId() const { return mPosId; } + int getNTiles() const { return mNTiles; } private: int mPosId; // id of the ring diff --git a/Detectors/Upgrades/ALICE3/RICH/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/RICH/simulation/src/Detector.cxx index de5c1817a515a..02719d6f93a00 100644 --- a/Detectors/Upgrades/ALICE3/RICH/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/RICH/simulation/src/Detector.cxx @@ -145,29 +145,29 @@ void Detector::createGeometry() vRICH->SetTitle(vstrng); auto& richPars = RICHBaseParam::Instance(); - prepareLayout(); + prepareLayout(); // Preparing the positions of the rings and tiles for (int iRing{0}; iRing < richPars.nRings; ++iRing) { if (!richPars.oddGeom && iRing == (richPars.nRings / 2)) { continue; } - mRings[iRing] = Ring{iRing, - richPars.nTiles, - richPars.rMin, - richPars.rMax, - richPars.radiatorThickness, - (float)mVTile1[iRing], - (float)mVTile2[iRing], - (float)mLAerogelZ[iRing], - richPars.detectorThickness, - (float)mVMirror1[iRing], - (float)mVMirror2[iRing], - richPars.zBaseSize, - (float)mR0Radiator[iRing], - (float)mR0PhotoDet[iRing], - (float)mTRplusG[iRing], - (float)mThetaBi[iRing], - GeometryTGeo::getRICHVolPattern()}; + mRings[iRing] = o2::rich::Ring{iRing, + richPars.nTiles, + richPars.rMin, + richPars.rMax, + richPars.radiatorThickness, + (float)mVTile1[iRing], + (float)mVTile2[iRing], + (float)mLAerogelZ[iRing], + richPars.detectorThickness, + (float)mVMirror1[iRing], + (float)mVMirror2[iRing], + richPars.zBaseSize, + (float)mR0Radiator[iRing], + (float)mR0PhotoDet[iRing], + (float)mTRplusG[iRing], + (float)mThetaBi[iRing], + GeometryTGeo::getRICHVolPattern()}; } if (richPars.enableFWDRich) { @@ -182,7 +182,7 @@ void Detector::InitializeO2Detector() { LOG(info) << "Initialize RICH O2Detector"; mGeometryTGeo = GeometryTGeo::Instance(); - // defineSensitiveVolumes(); + defineSensitiveVolumes(); } void Detector::defineSensitiveVolumes() @@ -194,12 +194,19 @@ void Detector::defineSensitiveVolumes() LOGP(info, "Adding RICH Sensitive Volumes"); // The names of the RICH sensitive volumes have the format: Ring(0...mRings.size()-1) - for (int j{0}; j < mRings.size(); j++) { - volumeName = GeometryTGeo::getRICHSensorPattern() + TString::Itoa(j, 10); - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding RICH Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (auto ring : mRings) { + for (int j = 0; j < ring.getNTiles(); j++) { + volumeName = Form("%s_%d_%d", GeometryTGeo::getRICHSensorPattern(), ring.getPosId(), j); + LOGP(info, "Trying {}", volumeName.Data()); + v = geoManager->GetVolume(volumeName.Data()); + if (!v) { + LOG(error) << "Geometry does not contain volume " << volumeName.Data(); + geoManager->GetListOfVolumes()->Print(); + LOG(fatal) << "Could not find volume " << volumeName.Data() << " in the geometry"; + } + LOGP(info, "Adding RICH Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } } diff --git a/Detectors/Upgrades/ALICE3/RICH/simulation/src/RICHRing.cxx b/Detectors/Upgrades/ALICE3/RICH/simulation/src/RICHRing.cxx index 27890dc0c4c06..1c6c9612795a0 100644 --- a/Detectors/Upgrades/ALICE3/RICH/simulation/src/RICHRing.cxx +++ b/Detectors/Upgrades/ALICE3/RICH/simulation/src/RICHRing.cxx @@ -106,7 +106,7 @@ Ring::Ring(int rPosId, photoTile->SetVertex(6, photThick / 2, photYmax / 2); photoTile->SetVertex(7, photThick / 2, -photYmax / 2); - TGeoVolume* photoTileVol = new TGeoVolume(Form("photoTile_%d_%d", rPosId, photTileCount), photoTile, medSi); + TGeoVolume* photoTileVol = new TGeoVolume(Form("%s_%d_%d", GeometryTGeo::getRICHSensorPattern(), rPosId, photTileCount), photoTile, medSi); photoTileVol->SetLineColor(kOrange - 8); photoTileVol->SetLineWidth(1); diff --git a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt index 645e3149e4ab7..6e3437c9d841b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -10,5 +10,7 @@ # or submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(macros) add_subdirectory(simulation) -add_subdirectory(workflow) \ No newline at end of file +add_subdirectory(reconstruction) +add_subdirectory(workflow) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt index a237a2d12211d..96ebf4ead4b7b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt @@ -12,8 +12,10 @@ o2_add_library(TRKBase SOURCES src/GeometryTGeo.cxx src/TRKBaseParam.cxx + src/SegmentationChip.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase) o2_target_root_dictionary(TRKBase HEADERS include/TRKBase/GeometryTGeo.h - include/TRKBase/TRKBaseParam.h) \ No newline at end of file + include/TRKBase/TRKBaseParam.h + include/TRKBase/SegmentationChip.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index 852cb138e2be7..deec53950cd5f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -14,6 +14,8 @@ #include #include +#include "DetectorsCommonDataFormats/DetID.h" +#include "TRKBase/TRKBaseParam.h" namespace o2 { @@ -42,12 +44,18 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } static const char* getTRKLayerPattern() { return sLayerName.c_str(); } + static const char* getTRKPetalAssemblyPattern() { return sPetalAssemblyName.c_str(); } static const char* getTRKPetalPattern() { return sPetalName.c_str(); } static const char* getTRKPetalDiskPattern() { return sPetalDiskName.c_str(); } static const char* getTRKPetalLayerPattern() { return sPetalLayerName.c_str(); } static const char* getTRKStavePattern() { return sStaveName.c_str(); } + static const char* getTRKHalfStavePattern() { return sHalfStaveName.c_str(); } + static const char* getTRKModulePattern() { return sModuleName.c_str(); } static const char* getTRKChipPattern() { return sChipName.c_str(); } static const char* getTRKSensorPattern() { return sSensorName.c_str(); } + static const char* getTRKDeadzonePattern() { return sDeadzoneName.c_str(); } + static const char* getTRKMetalStackPattern() { return sMetalStackName.c_str(); } + static const char* getTRKWrapVolPattern() { return sWrapperVolumeName.c_str(); } int getNumberOfChips() const { return mSize; } @@ -60,45 +68,84 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache int extractNumberOfDisksVD() const; int extractNumberOfChipsPerPetalVD() const; int extractNumberOfStavesMLOT(int lay) const; + int extractNumberOfHalfStavesMLOT(int lay) const; + int extractNumberOfModulesMLOT(int lay) const; + int extractNumberOfChipsMLOT(int lay) const; /// Extract number following the prefix in the name string int extractVolumeCopy(const char* name, const char* prefix) const; int getNumberOfLayersMLOT() const { return mNumberOfLayersMLOT; } - int getNumberOffActivePartsVD() const { return mNumberOfActivePartsVD; } + int getNumberOfActivePartsVD() const { return mNumberOfActivePartsVD; } + int getNumberOfHalfStaves(int lay) const { return mNumberOfHalfStaves[lay]; } bool isOwner() const { return mOwner; } void setOwner(bool v) { mOwner = v; } void Print(Option_t* opt = "") const; - void PrintChipID(int index, int subDetID, int petalcase, int disk, int lay, int stave, int indexRetrieved) const; + void PrintChipID(int index, int subDetID, int petalcase, int disk, int lay, int stave, int halfstave, int mod, int chip) const; - int getLayer(int index) const; - int getStave(int index) const; int getSubDetID(int index) const; int getPetalCase(int index) const; int getDisk(int index) const; + int getLayer(int index) const; + int getStave(int index) const; + int getHalfStave(int index) const; + int getModule(int index) const; + int getChip(int index) const; + + void defineMLOTSensors(); + int getBarrelLayer(int) const; + + // sensor ref X and alpha for ML & OT + void extractSensorXAlphaMLOT(int, float&, float&); + + // cache for tracking frames (ML & OT) + bool isTrackingFrameCachedMLOT() const { return !mCacheRefXMLOT.empty(); } + void fillTrackingFramesCacheMLOT(); + + float getSensorRefAlphaMLOT(int index) const { return mCacheRefAlphaMLOT[index]; } + float getSensorXMLOT(int index) const { return mCacheRefXMLOT[index]; } + + // create matrix for tracking to local frame for MLOT + TGeoHMatrix& createT2LMatrixMLOT(int); /// This routine computes the chip index number from the subDetID, petal, disk, layer, stave /// TODO: retrieve also from chip when chips will be available - /// in substave + /// This routine computes the chip index number from the subDetID, petal, disk, layer, stave, half stave, module, chip /// \param int subDetID The subdetector ID, 0 for VD, 1 for MLOT /// \param int petalcase The petal case number for VD, from 0 to 3 /// \param int disk The disk number for VD, from 0 to 5 /// \param int lay The layer number. Starting from 0 both for VD and MLOT /// \param int stave The stave number for MLOT. Starting from 0 - int getChipIndex(int subDetID, int petalcase, int disk, int lay, int stave) const; + /// \param int halfstave The half stave number for MLOT. Can be 0 or 1 + /// \param int module The module number for MLOT, from 0 to 10 (or 20) + /// \param int chip The chip number for MLOT, from 0 to 8 + unsigned short getChipIndex(int subDetID, int petalcase, int disk, int lay, int stave, int halfstave, int mod, int chip) const; - /// This routine computes subDetID, petal, disk, layer, stave given the chip index number /// TODO: copute also from chip when chips will be available + /// This routine computes the chip index number from the subDetID, volume, layer, stave, half stave, module, chip + /// \param int subDetID The subdetector ID, 0 for VD, 1 for MLOT + /// \param int volume is needed only with the current configuration for VD where each single element is a volume. // TODO: when the geometry naming scheme will be changed, change this method + /// \param int lay The layer number for the MLOT. In the current configuration for VD this is not needed. // TODO: when the geometry naming scheme will be changed, change this method + /// \param int stave The stave number in each layer for MLOT. Starting from 0. + /// \param int halfstave The half stave number for MLOT. Can be 0 or 1 + /// \param int module The module number for MLOT, from 0 to 10 (or 20) + /// \param int chip The chip number for MLOT, from 0 to 8 + unsigned short getChipIndex(int subDetID, int volume, int lay, int stave, int halfstave, int mod, int chip) const; + + /// This routine computes subDetID, petal, disk, layer, stave, half stave, module, chip, given the chip index number /// \param int index The chip index number, starting from 0 /// \param int subDetID The subdetector ID, 0 for VD, 1 for MLOT /// \param int petalcase The petal case number for VD, from 0 to 3 /// \param int disk The disk number for VD, from 0 to 5 /// \param int lay The layer number. Starting from 0 both for VD and MLOT /// \param int stave The stave number for MLOT. Starting from 0 - bool getChipID(int index, int& subDetID, int& petalcase, int& disk, int& lay, int& stave) const; + /// \param int halfstave The half stave number for MLOT. Can be 0 or 1 + /// \param int module The module number for MLOT, from 0 to 10 (or 20) + /// \param int chip The chip number for MLOT, from 0 to 8 + bool getChipID(int index, int& subDetID, int& petalcase, int& disk, int& lay, int& stave, int& halfstave, int& mod, int& chip) const; - int getLastChipIndex(int lay) const { return mLastChipIndex[lay]; } - int getFirstChipIndex(int lay, int petalcase, int subDetID) const + unsigned short getLastChipIndex(int lay) const { return mLastChipIndex[lay]; } + unsigned short getFirstChipIndex(int lay, int petalcase, int subDetID) const { /// Get the first chip index of the active petal (VD) or layer (MLOT) if (subDetID == 0) { // VD @@ -115,13 +162,17 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache TString getMatrixPath(int index) const; +#ifdef ENABLE_UPGRADES static const char* composeSymNameTRK(int d) { return Form("%s_%d", o2::detectors::DetID(o2::detectors::DetID::TRK).getName(), d); } +#endif + static const char* composeSymNameLayer(int d, int layer); static const char* composeSymNameStave(int d, int layer); - static const char* composeSymNameChip(int d, int lr); + static const char* composeSymNameModule(int d, int layer); + static const char* composeSymNameChip(int d, int layer); static const char* composeSymNameSensor(int d, int layer); protected: @@ -129,35 +180,55 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static std::string sVolumeName; static std::string sLayerName; + static std::string sPetalAssemblyName; static std::string sPetalName; static std::string sPetalDiskName; static std::string sPetalLayerName; static std::string sStaveName; + static std::string sHalfStaveName; + static std::string sModuleName; static std::string sChipName; static std::string sSensorName; - static std::string sWrapperVolumeName; ///< Wrapper volume name + static std::string sDeadzoneName; + static std::string sMetalStackName; + + static std::string sWrapperVolumeName; ///< Wrapper volume name, not implemented at the moment Int_t mNumberOfLayersMLOT; ///< number of layers Int_t mNumberOfActivePartsVD; ///< number of layers Int_t mNumberOfLayersVD; ///< number of layers Int_t mNumberOfPetalsVD; ///< number of Petals = chip in each VD layer Int_t mNumberOfDisksVD; ///< number of Disks = 6 - std::vector mLastChipIndex; ///< max ID of the detctor in the petal(VD) or layer(MLOT) - std::vector mLastChipIndexVD; ///< max ID of the detctor in the layer for the VD - std::vector mLastChipIndexMLOT; ///< max ID of the detctor in the layer for the MLOT + std::vector mNumberOfStaves; ///< Number Of Staves per layer in ML/OT + std::vector mNumberOfHalfStaves; ///< Number Of Half staves in each stave of the layer in ML/OT + std::vector mNumberOfModules; ///< Number Of Modules per stave (half stave) in ML/OT + std::vector mNumberOfChips; ///< number of chips per module in ML/OT std::vector mNumberOfChipsPerLayerVD; ///< number of chips per layer VD ( = number of petals) - std::vector mNumberOfChipsPerLayerMLOT; ///< number of chips per layer MLOT ( = 1 for the moment) + std::vector mNumberOfChipsPerLayerMLOT; ///< number of chips per layer MLOT std::vector mNumbersOfChipPerDiskVD; ///< numbersOfChipPerDiskVD std::vector mNumberOfChipsPerPetalVD; ///< numbersOfChipPerPetalVD - std::vector mNumberOfStaves; ///< Number Of Staves per layer in ML/OT - std::array mLayerToWrapper; ///< Layer to wrapper correspondence + // std::vector mNumberOfChipsPerStave; ///< number of chips per stave in ML/OT + // std::vector mNumberOfChipsPerHalfStave; ///< number of chips per half stave in ML/OT + // std::vector mNumberOfChipsPerModule; ///< number of chips per module in ML/OT + std::vector mLastChipIndex; ///< max ID of the detctor in the petal(VD) or layer(MLOT) + std::vector mLastChipIndexVD; ///< max ID of the detctor in the layer for the VD + std::vector mLastChipIndexMLOT; ///< max ID of the detctor in the layer for the MLOT + + std::array mLayerToWrapper; ///< Layer to wrapper correspondence, not implemented yet bool mOwner = true; //! is it owned by the singleton? + std::vector sensorsMLOT; + std::vector mCacheRefXMLOT; /// cache for X of ML and OT + std::vector mCacheRefAlphaMLOT; /// cache for sensor ref alpha ML and OT + + eLayout mLayoutML; // Type of segmentation for the middle layers + eLayout mLayoutOL; // Type of segmentation for the outer layers + private: static std::unique_ptr sInstance; }; } // namespace trk } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h new file mode 100644 index 0000000000000..8110191931e44 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h @@ -0,0 +1,269 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file SegmentationChip.h +/// \brief Definition of the SegmentationChipclass + +#ifndef ALICEO2_TRK_SEGMENTATIONCHIP_H_ +#define ALICEO2_TRK_SEGMENTATIONCHIP_H_ + +#include +#include + +#include "MathUtils/Cartesian.h" +#include "TRKBase/Specs.h" + +namespace o2::trk +{ + +/// Segmentation and response for TRK chips in ALICE3 upgrade +/// This is a work-in-progress code derived from the ITS2 and ITS3 segmentations. +class SegmentationChip +{ + // This class defines the segmenation of the TRK chips in the ALICE3 upgrade. + // The "global coordinate system" refers to the hit position in cm in the global coordinate system centered in 0,0,0 + // The "local coordinate system" refers to the hit position in cm in the coordinate system of the sensor, which + // is centered in 0,0,0 in the case of curved layers, and in the middle of the chip in the case of flat layers + // The "detector coordinate system" refers to the hit position in row,col inside the sensor + // This class provides the transformations from the local and detector coordinate systems + // The conversion between global and local coordinate systems is operated by the transformation matrices + // For the curved VD layers there exist four coordinate systems. + // 1. The global (curved) coordinate system. The chip's center of coordinate system is + // defined at the the mid-point of the detector. + // 2. The local (curved) coordinate system, centered in 0,0,0. + // 3. The local (flat) coordinate system. This is the tube segment projected onto a flat + // surface, centered in the middle of the chip, with the y axis pointing towards the interaction point. + // In the projection we implicitly assume that the inner and outer stretch does not depend on the radius. + // 4. The detector coordinate system. Defined by the row and column segmentation. + // For the flat ML and OT layers, there exist two coordinate systems: + // 1. The global (flat) coordinate system. The chip's center of coordinate system is + // defined at the the mid-point of the detector. + // 2. The detector coordinate system. Defined by the row and column segmentation. + // TODO: add segmentation for VD disks + + public: + constexpr SegmentationChip() = default; + ~SegmentationChip() = default; + constexpr SegmentationChip(const SegmentationChip&) = default; + constexpr SegmentationChip(SegmentationChip&&) = delete; + constexpr SegmentationChip& operator=(const SegmentationChip&) = default; + constexpr SegmentationChip& operator=(SegmentationChip&&) = delete; + + static constexpr float PitchColVD{constants::VD::petal::layer::pitchZ}; + static constexpr float PitchRowVD{constants::VD::petal::layer::pitchX}; + + static constexpr float PitchColMLOT{constants::moduleMLOT::chip::pitchZ}; + static constexpr float PitchRowMLOT{constants::moduleMLOT::chip::pitchX}; + + static constexpr float SensorLayerThicknessVD = {constants::VD::petal::layer::totalThickness}; // physical thickness of sensitive part = 30 um + static constexpr float SensorLayerThicknessML = {constants::moduleMLOT::chip::totalThickness}; // physical thickness of sensitive part = 100 um + static constexpr float SensorLayerThicknessOT = {constants::moduleMLOT::chip::totalThickness}; // physical thickness of sensitive part = 100 um + + static constexpr float SiliconThicknessVD = constants::VD::silicon::thickness; // effective thickness of sensitive part + static constexpr float SiliconThicknessMLOT = constants::moduleMLOT::silicon::thickness; // effective thickness of sensitive part + + static constexpr std::array radiiVD = constants::VD::petal::layer::radii; + + /// Transformation from Geant detector centered local coordinates (cm) to + /// Pixel cell numbers iRow and iCol. + /// Returns kTRUE if point x,z is inside sensitive volume, kFALSE otherwise. + /// A value of -1 for iRow or iCol indicates that this point is outside of the + /// detector segmentation as defined. + /// \param float x Detector local coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param float z Detector local coordinate z in cm with respect to + /// the center of the sensitive volulme. + /// \param int iRow Detector x cell coordinate. Has the range 0 <= iRow < mNumberOfRows + /// \param int iCol Detector z cell coordinate. Has the range 0 <= iCol < mNumberOfColumns + /// \param int subDetID Sub-detector ID (0 for VD, 1 for ML/OT) + /// \param int layer Layer number (0 to 2 for VD, 0 to 7 for ML/OT) + /// \param int disk Disk number (0 to 5 for VD) + static bool localToDetector(float xRow, float zCol, int& iRow, int& iCol, int subDetID, int layer, int disk) noexcept + { + if (!isValidLoc(xRow, zCol, subDetID, layer)) { + LOGP(debug, "Local coordinates not valid: row = {} cm, col = {} cm", xRow, zCol); + return false; + } + localToDetectorUnchecked(xRow, zCol, iRow, iCol, subDetID, layer, disk); + + LOG(debug) << "Result from localToDetectorUnchecked: xRow " << xRow << " -> iRow " << iRow << ", zCol " << zCol << " -> iCol " << iCol << " on subDetID, layer, disk: " << subDetID << " " << layer << " " << disk; + + if (!isValidDet(iRow, iCol, subDetID, layer)) { + iRow = iCol = -1; + LOGP(debug, "Detector coordinates not valid: iRow = {}, iCol = {}", iRow, iCol); + return false; + } + return true; + }; + /// same but w/o check for row/column range + static void localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, int subDetID, int layer, int disk) noexcept + { + // convert to row/col w/o over/underflow check + float pitchRow(0), pitchCol(0); + float maxWidth(0), maxLength(0); + + if (subDetID == 0) { + pitchRow = PitchRowVD; + pitchCol = PitchColVD; + maxWidth = constants::VD::petal::layer::width[layer]; + maxLength = constants::VD::petal::layer::length; + // TODO: change this to use the layer and disk + } else if (subDetID == 1) { + pitchRow = PitchRowMLOT; + pitchCol = PitchColMLOT; + maxWidth = constants::moduleMLOT::chip::width - constants::moduleMLOT::chip::passiveEdgeReadOut; + maxLength = constants::moduleMLOT::chip::length; + } + // convert to row/col + iRow = static_cast(std::floor((maxWidth / 2 - xRow) / pitchRow)); + iCol = static_cast(std::floor((zCol + maxLength / 2) / pitchCol)); + }; + + // Check local coordinates (cm) validity. + static constexpr bool isValidLoc(float x, float z, int subDetID, int layer) noexcept + { + float maxWidth(0), maxLength(0); + if (subDetID == 0) { + maxWidth = constants::VD::petal::layer::width[layer]; + maxLength = constants::VD::petal::layer::length; + // TODO: change this to use the layer and disk + } else if (subDetID == 1) { // ML/OT + maxWidth = constants::moduleMLOT::chip::width - constants::moduleMLOT::chip::passiveEdgeReadOut; + maxLength = constants::moduleMLOT::chip::length; + } + return (-maxWidth / 2 < x && x < maxWidth / 2 && -maxLength / 2 < z && z < maxLength / 2); + } + + // Check detector coordinates validity. + static constexpr bool isValidDet(int row, int col, int subDetID, int layer) noexcept + { + // Check if the row and column are within the valid range + int nRows(0), nCols(0); + if (subDetID == 0) { + nRows = constants::VD::petal::layer::nRows[layer]; + nCols = constants::VD::petal::layer::nCols; + // TODO: change this to use the layer and disk + } else if (subDetID == 1) { + nRows = constants::moduleMLOT::chip::nRows; + nCols = constants::moduleMLOT::chip::nCols; + } + return (row >= 0 && row < nRows && col >= 0 && col < nCols); + } + + /// Transformation from Detector cell coordinates to Geant detector centered + /// local coordinates (cm) + /// \param int iRow Detector x cell coordinate. + /// \param int iCol Detector z cell coordinate. + /// \param float x Detector local coordinate x in cm with respect to the + /// center of the sensitive volume. + /// \param float z Detector local coordinate z in cm with respect to the + /// center of the sensitive volume. + /// If iRow and or iCol is outside of the segmentation range a value of -0.5*Dx() + /// or -0.5*Dz() is returned. + /// \param int subDetID Sub-detector ID (0 for VD, 1 for ML/OT) + /// \param int layer Layer number (0 to 2 for VD, 0 to 7 for ML/OT) + /// \param int disk Disk number (0 to 5 for VD) + static constexpr bool detectorToLocal(int iRow, int iCol, float& xRow, float& zCol, int subDetID, int layer, int disk) noexcept + { + if (!isValidDet(iRow, iCol, subDetID, layer)) { + LOGP(debug, "Detector coordinates not valid: iRow = {}, iCol = {}", iRow, iCol); + return false; + } + detectorToLocalUnchecked(iRow, iCol, xRow, zCol, subDetID, layer, disk); + LOG(debug) << "Result from detectorToLocalUnchecked: iRow " << iRow << " -> xRow " << xRow << ", iCol " << iCol << " -> zCol " << zCol << " on subDetID, layer, disk: " << subDetID << " " << layer << " " << disk; + + if (!isValidLoc(xRow, zCol, subDetID, layer)) { + LOGP(debug, "Local coordinates not valid: row = {} cm, col = {} cm", xRow, zCol); + return false; + } + return true; + }; + + // Same as detectorToLocal w.o. checks. + // We position ourself in the middle of the pixel. + static void detectorToLocalUnchecked(int row, int col, float& xRow, float& zCol, int subDetID, int layer, int disk) noexcept + { + /// xRow = half chip width - iRow(center) * pitch + /// zCol = iCol * pitch - half chip lenght + if (subDetID == 0) { + xRow = 0.5 * (constants::VD::petal::layer::width[layer] - PitchRowVD) - (row * PitchRowVD); + zCol = col * PitchColVD + 0.5 * (PitchColVD - constants::VD::petal::layer::length); + } else if (subDetID == 1) { // ML/OT + xRow = 0.5 * (constants::moduleMLOT::chip::width - constants::moduleMLOT::chip::passiveEdgeReadOut - PitchRowMLOT) - (row * PitchRowMLOT); + zCol = col * PitchRowMLOT + 0.5 * (PitchRowMLOT - constants::moduleMLOT::chip::length); + } + } + + /// Transformation from the curved surface to a flat surface. + /// Additionally a shift in the flat coordinates must be applied because + /// the center of the TGeoShap when projected will be higher than the + /// physical thickness of the chip. Thus we shift the projected center + /// down by this difference to align the coordinate systems. + /// \param layer VD layer number + /// \param xCurved Detector local curved coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param yCurved Detector local curved coordinate y in cm with respect to + /// the center of the sensitive volume. + /// \return math_utils::Vector2D: x and y represent the detector local flat coordinates x and y + // in cm with respect to the center of the sensitive volume. + static math_utils::Vector2D curvedToFlat(const int layer, const float xCurved, const float yCurved) noexcept + { + // Align the flat surface with the curved survace of the original chip (and account for metal stack, TODO) + float dist = std::hypot(xCurved, yCurved); + float phi = std::atan2(yCurved, xCurved); + + // the y position is in the silicon volume however we need the chip volume (silicon+metalstack) + // this is accounted by a y shift + float xFlat = constants::VD::petal::layer::radii[layer] * phi; /// this is equal to the circumference segment covered between y=0 and the phi angle + float yFlat = constants::VD::petal::layer::radii[layer] - dist; + return math_utils::Vector2D(xFlat, yFlat); + } + + /// Transformation from the flat surface to a curved surface + /// It works only if the detector is not rototraslated. + /// \param layer VD layer number + /// \param xFlat Detector local flat coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param yFlat Detector local flat coordinate y in cm with respect to + /// the center of the sensitive volume. + /// \return math_utils::Vector2D: x and y represent the detector local curved coordinates x and y + // in cm with respect to the center of the sensitive volume. + static constexpr math_utils::Vector2D flatToCurved(int layer, float xFlat, float yFlat) noexcept + { + // Revert the curvedToFlat transformation + float dist = constants::VD::petal::layer::radii[layer] - yFlat; + float phi = xFlat / constants::VD::petal::layer::radii[layer]; + // the y position is in the chip volume however we need the silicon volume + // this is accounted by a -y shift + float xCurved = dist * std::cos(phi); + float yCurved = dist * std::sin(phi); + return math_utils::Vector2D(xCurved, yCurved); + } + + /// Print segmentation info + static void Print() noexcept + { + LOG(info) << "Number of rows:\nVD L0: " << constants::VD::petal::layer::nRows[0] + << "\nVD L1: " << constants::VD::petal::layer::nRows[1] + << "\nVD L2: " << constants::VD::petal::layer::nRows[2] + << "\nML/OT chip: " << constants::moduleMLOT::chip::nRows; + + LOG(info) << "Number of cols:\nVD: " << constants::VD::petal::layer::nCols + << "\nML/OT chip: " << constants::moduleMLOT::chip::nCols; + + LOG(info) << "Pitch rows x cols [um]:\nVD: " << PitchRowVD * 1e4 << "x" << PitchColVD * 1e4 + << "\nML/OT chip: " << PitchRowMLOT * 1e4 << "x" << PitchColMLOT * 1e4; + } +}; + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h new file mode 100644 index 0000000000000..c3c7de9dbe910 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -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 Specs.h +/// \brief specs of the ALICE3 TRK + +#ifndef O2_ALICE_TRK_SPECS +#define O2_ALICE_TRK_SPECS + +#include +#include +// This is a temporary version with the specs for the ALICE3 TRK +// This files defines the design specifications of the chips for VD, ML, OT. +// Each TGeoShape has the following properties +// length: dimension in z-axis +// width: dimension in xy-axes +// color: for visualisation +namespace o2::trk::constants +{ +// Default unit of TGeo = cm +constexpr double cm{1}; +constexpr double mu{1e-4}; +constexpr double mm{1e-1}; + +namespace VD // TODO: add a primitive segmentation with more granularity wrt 1/4 layer = 1 chip +{ +namespace silicon +{ +constexpr double thickness{20 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 20 um substrate)? +} // namespace silicon +namespace metalstack +{ +constexpr double thickness{80 * mu}; // thickness of the copper metal stack - for the moment it is not implemented. PL: set to 80 um considering silicon as material +} // namespace metalstack +namespace petal +{ +constexpr int nLayers{3}; // number of layers in each VD petal +constexpr int nDisks{6}; // number of disks in each VD petal +namespace layer +{ +constexpr double pitchX{10 * mu}; // pitch of the row +constexpr double pitchZ{10 * mu}; // pitch of the column +constexpr double totalThickness{silicon::thickness + metalstack::thickness}; // total thickness of the chip +constexpr std::array gaps{1.63 * mm, 1.2 * mm, 1.2 * mm}; // gaps between two consecutive petals +constexpr std::array radii{0.5 * cm, 1.2 * cm, 2.5 * cm}; // radius of layer in cm +constexpr std::array width{radii[0] * 2 * M_PI / 4 - gaps[0], radii[1] * 2 * M_PI / 4 - gaps[1], radii[2] * 2 * M_PI / 4 - gaps[2]}; // width of the quarter of layer in cm +constexpr double length{50 * cm}; // length of the layer +constexpr int nCols{static_cast(length / pitchZ)}; // number of columns in the chip +constexpr std::array nRows{static_cast(width[0] / pitchX), static_cast(width[1] / pitchX), static_cast(width[2] / pitchX)}; // number of rows in the chip. For the moment is different for each layer since a siner segmentation in repetitive units is stil to be implemented + +} // namespace layer +namespace disk +{ //// TODO: to be filled +constexpr double radiusIn{0.5 * cm}; +constexpr double radiusOut{2.5 * cm}; +} // namespace disk +} // namespace petal +} // namespace VD + +namespace moduleMLOT /// same for ML and OT for the moment +{ /// TODO: account for different modules in case of changes +namespace silicon +{ +constexpr double thickness{100 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 90 um substrate)? +} // namespace silicon +namespace metalstack +{ +constexpr double thickness{0 * mu}; // thickness of the copper metal stack - for the moment it is not implemented +} // namespace metalstack +namespace chip +{ +constexpr double width{25 * mm}; // width of the chip +constexpr double length{32 * mm}; // length of the chip +constexpr double pitchX{50 * mu}; // pitch of the row +constexpr double pitchZ{50 * mu}; // pitch of the column +constexpr double totalThickness{silicon::thickness + metalstack::thickness}; // total thickness of the chip +static constexpr double passiveEdgeReadOut{1.5 * mm}; // width of the readout edge -> dead zone +constexpr int nRows{static_cast((width - passiveEdgeReadOut) / pitchX)}; // number of rows in the chip +constexpr int nCols{static_cast(length / pitchZ)}; // number of columns in the chip +} // namespace chip +namespace gaps +{ +constexpr double interChips{50 * mu}; // gap between the chips +constexpr double outerEdgeLongSide{1 * mm}; // gap between the chips and the outer edges (long side) +constexpr double outerEdgeShortSide{0.1 * mm}; // gap between the chips and the outer edges (short side) +} // namespace gaps +constexpr double width{chip::width * 2 + gaps::interChips + 2 * gaps::outerEdgeLongSide}; // width of the module +constexpr double length{chip::length * 4 + 3 * gaps::interChips + 2 * gaps::outerEdgeShortSide}; // length of the module +constexpr int nRows{static_cast(width / chip::pitchX)}; // number of columns in the module +constexpr int nCols{static_cast(length / chip::pitchZ)}; // number of rows in the module +} // namespace moduleMLOT + +namespace ML +{ +constexpr double width{constants::moduleMLOT::width * 1}; // width of the stave +// constexpr double length{constants::moduleMLOT::length * 10}; // length of the stave +constexpr double length{124 * cm}; // length of the stave, hardcoded to fit the implemented geometry +constexpr int nRows{static_cast(width / constants::moduleMLOT::chip::pitchX)}; // number of rows in the stave +constexpr int nCols{static_cast(length / constants::moduleMLOT::chip::pitchZ)}; // number of columns in the stave +} // namespace ML + +namespace OT +{ +namespace halfstave +{ +constexpr double width{moduleMLOT::width * 1}; // width of the half stave +// constexpr double length{moduleMLOT::length * 20}; // length of the halfstave +constexpr double length{258 * cm}; // length of the halfstave, hardcoded to fit the implemented geometry +constexpr int nRows{static_cast(width / moduleMLOT::chip::pitchX)}; // number of rows in the halfstave +constexpr int nCols{static_cast(length / moduleMLOT::chip::pitchZ)}; // number of columns in the halfstave +} // namespace halfstave +constexpr double width{halfstave::width * 2}; // width of the stave +constexpr double length{halfstave::length}; // length of the stave +constexpr int nRows{static_cast(width / moduleMLOT::chip::pitchX)}; // number of rows in the stave +constexpr int nCols{static_cast(length / moduleMLOT::chip::pitchZ)}; // number of columns in the stave +} // namespace OT + +namespace apts /// parameters for the APTS response +{ +constexpr double pitchX{15.0 * mu}; +constexpr double pitchZ{15.0 * mu}; +constexpr double responseYShift{15.5 * mu}; +constexpr double thickness{45 * mu}; +} // namespace apts + +namespace alice3resp /// parameters for the alice3 chip response +{ +constexpr double pitchX{10.0 * mu}; +constexpr double pitchZ{10.0 * mu}; +constexpr double responseYShift{5 * mu}; /// center of the epitaxial layer +constexpr double thickness{20 * mu}; +} // namespace alice3resp + +} // namespace o2::trk::constants + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 63c95b1e6b2f6..d5e11313c0f0c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -20,19 +20,38 @@ namespace o2 namespace trk { +enum eOverallGeom { + kDefaultRadii = 0, // After Upgrade Days March 2024 + kModRadii, +}; + enum eLayout { kCylinder = 0, kTurboStaves, kStaggered, }; +enum eVDLayout { + kIRIS4 = 0, + kIRISFullCyl, + kIRIS5, + kIRIS4a, +}; + struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 Bool_t irisOpen = false; - eLayout layoutML = kCylinder; // Type of segmentation for the middle layers - eLayout layoutOL = kCylinder; // Type of segmentation for the outer layers + eOverallGeom overallGeom = kDefaultRadii; // Overall geometry option, to be used in Detector::buildTRKMiddleOuterLayers + + eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers + eLayout layoutOL = kStaggered; // Type of segmentation for the outer layers + eVDLayout layoutVD = kIRIS4; // VD detector layout design + + eLayout getLayoutML() const { return layoutML; } + eLayout getLayoutOL() const { return layoutOL; } + eVDLayout getLayoutVD() const { return layoutVD; } O2ParamDef(TRKBaseParam, "TRKBase"); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 4547225033498..d5d37ec00acef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -11,9 +11,12 @@ #include #include -// #include "TRKBase/SegmentationChip.h" +#include "TRKBase/SegmentationChip.h" +#include -// using Segmentation = o2::trk::SegmentationChip; +#include + +using Segmentation = o2::trk::SegmentationChip; namespace o2 { @@ -24,12 +27,18 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; std::string GeometryTGeo::sLayerName = "TRKLayer"; +std::string GeometryTGeo::sPetalAssemblyName = "PETAL"; std::string GeometryTGeo::sPetalName = "PETALCASE"; std::string GeometryTGeo::sPetalDiskName = "DISK"; std::string GeometryTGeo::sPetalLayerName = "LAYER"; std::string GeometryTGeo::sStaveName = "TRKStave"; +std::string GeometryTGeo::sHalfStaveName = "TRKHalfStave"; +std::string GeometryTGeo::sModuleName = "TRKModule"; std::string GeometryTGeo::sChipName = "TRKChip"; std::string GeometryTGeo::sSensorName = "TRKSensor"; +std::string GeometryTGeo::sDeadzoneName = "TRKDeadzone"; +std::string GeometryTGeo::sMetalStackName = "TRKMetalStack"; + std::string GeometryTGeo::sWrapperVolumeName = "TRKUWrapVol"; ///< Wrapper volume name, not implemented at the moment o2::trk::GeometryTGeo::~GeometryTGeo() @@ -54,9 +63,9 @@ GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : DetMatrixCache(detectors void GeometryTGeo::Build(int loadTrans) { ///// current geometry organization: - ///// total elements = 258 = x staves * 8 layers ML+OT + 4 petal cases * (3 layers + 6 disks) + ///// total elements = x staves (*2 half staves if staggered geometry) * 8 layers ML+OT + 4 petal cases * (3 layers + 6 disks) ///// indexing from 0 to 35: VD petals -> layers -> disks - ///// indexing from 36 to 257: MLOT staves + ///// indexing from 36 to y: MLOT staves if (isBuilt()) { LOGP(warning, "Already built"); @@ -67,24 +76,36 @@ void GeometryTGeo::Build(int loadTrans) LOGP(fatal, "Geometry is not loaded"); } + mLayoutML = o2::trk::TRKBaseParam::Instance().getLayoutML(); + mLayoutOL = o2::trk::TRKBaseParam::Instance().getLayoutOL(); + + LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOL; + mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); + mNumberOfPetalsVD = extractNumberOfPetalsVD(); mNumberOfActivePartsVD = extractNumberOfActivePartsVD(); mNumberOfLayersVD = extractNumberOfLayersVD(); - mNumberOfPetalsVD = extractNumberOfPetalsVD(); mNumberOfDisksVD = extractNumberOfDisksVD(); mNumberOfStaves.resize(mNumberOfLayersMLOT); - mLastChipIndex.resize(mNumberOfPetalsVD + mNumberOfLayersMLOT); - mLastChipIndexVD.resize(mNumberOfPetalsVD); - mLastChipIndexMLOT.resize(mNumberOfLayersMLOT); /// ML and OT are part of TRK as the same detector, without disks + mNumberOfHalfStaves.resize(mNumberOfLayersMLOT); + mNumberOfModules.resize(mNumberOfLayersMLOT); + mNumberOfChips.resize(mNumberOfLayersMLOT); + mNumberOfChipsPerLayerVD.resize(mNumberOfLayersVD); mNumberOfChipsPerLayerMLOT.resize(mNumberOfLayersMLOT); mNumbersOfChipPerDiskVD.resize(mNumberOfDisksVD); mNumberOfChipsPerPetalVD.resize(mNumberOfPetalsVD); + mLastChipIndex.resize(mNumberOfPetalsVD + mNumberOfLayersMLOT); + mLastChipIndexVD.resize(mNumberOfPetalsVD); + mLastChipIndexMLOT.resize(mNumberOfLayersMLOT); /// ML and OT are part of TRK as the same detector, without disks + for (int i = 0; i < mNumberOfLayersMLOT; i++) { - std::cout << "Layer MLOT: " << i << std::endl; mNumberOfStaves[i] = extractNumberOfStavesMLOT(i); + mNumberOfHalfStaves[i] = extractNumberOfHalfStavesMLOT(i); + mNumberOfModules[i] = extractNumberOfModulesMLOT(i); + mNumberOfChips[i] = extractNumberOfChipsMLOT(i); } int numberOfChipsTotal = 0; @@ -99,15 +120,16 @@ void GeometryTGeo::Build(int loadTrans) /// filling the information for the MLOT for (int i = 0; i < mNumberOfLayersMLOT; i++) { - mNumberOfChipsPerLayerMLOT[i] = extractNumberOfStavesMLOT(i); // for the moment, considering 1 stave = 1 chip. TODO: add the final segmentation in chips + mNumberOfChipsPerLayerMLOT[i] = mNumberOfStaves[i] * mNumberOfHalfStaves[i] * mNumberOfModules[i] * mNumberOfChips[i]; numberOfChipsTotal += mNumberOfChipsPerLayerMLOT[i]; mLastChipIndex[i + mNumberOfPetalsVD] = numberOfChipsTotal - 1; mLastChipIndexMLOT[i] = numberOfChipsTotal - 1; } - // setSize(mNumberOfLayersMLOT + mNumberOfActivePartsVD); /// temporary, number of chips = number of layers and active parts - setSize(numberOfChipsTotal); /// temporary, number of chips = number of staves and active parts + setSize(numberOfChipsTotal); fillMatrixCache(loadTrans); + defineMLOTSensors(); + fillTrackingFramesCacheMLOT(); } //__________________________________________________________________________ @@ -129,9 +151,7 @@ int GeometryTGeo::getPetalCase(int index) const int subDetID = getSubDetID(index); if (subDetID == 1) { return -1; - } - - else if (index <= mLastChipIndexVD[mNumberOfPetalsVD - 1]) { + } else if (index <= mLastChipIndexVD[mNumberOfPetalsVD - 1]) { while (index > mLastChipIndexVD[petalcase]) { petalcase++; } @@ -139,6 +159,22 @@ int GeometryTGeo::getPetalCase(int index) const return petalcase; } +//__________________________________________________________________________ +int GeometryTGeo::getDisk(int index) const +{ + int subDetID = getSubDetID(index); + int petalcase = getPetalCase(index); + + if (subDetID == 0) { /// VD + if (index % mNumberOfChipsPerPetalVD[petalcase] < mNumberOfLayersVD) { + return -1; /// layers + } + return (index % mNumberOfChipsPerPetalVD[petalcase]) - mNumberOfLayersVD; + } + + return -1; /// not found or ML/OT +} + //__________________________________________________________________________ int GeometryTGeo::getLayer(int index) const { @@ -155,7 +191,7 @@ int GeometryTGeo::getLayer(int index) const while (index > mLastChipIndex[lay]) { lay++; } - return lay - mNumberOfPetalsVD; /// numeration of MLOT layesrs starting from 1 + return lay - mNumberOfPetalsVD; /// numeration of MLOT layers starting from 0 } return -1; /// -1 if not found } @@ -170,30 +206,115 @@ int GeometryTGeo::getStave(int index) const return -1; } else if (subDetID == 1) { /// MLOT int lay = getLayer(index); - index -= getFirstChipIndex(lay, petalcase, subDetID); - return index; /// |||| + index -= getFirstChipIndex(lay, petalcase, subDetID); // get the index of the sensing element in the layer + + const int Nhs = mNumberOfHalfStaves[lay]; + const int Nmod = mNumberOfModules[lay]; + const int Nchip = mNumberOfChips[lay]; + + if (Nhs == 2) { + int chipsPerModule = Nchip; + int chipsPerHalfStave = Nmod * chipsPerModule; + int chipsPerStave = Nhs * chipsPerHalfStave; + return index / chipsPerStave; + } else if (Nhs == 1) { + int chipsPerModule = Nchip; + int chipsPerStave = Nmod * chipsPerModule; + return index / chipsPerStave; + } } - return -1; /// not found + return -1; } //__________________________________________________________________________ -int GeometryTGeo::getDisk(int index) const +int GeometryTGeo::getHalfStave(int index) const { int subDetID = getSubDetID(index); + int lay = getLayer(index); int petalcase = getPetalCase(index); if (subDetID == 0) { /// VD - if (index % mNumberOfChipsPerPetalVD[petalcase] < mNumberOfLayersVD) { - return -1; /// layers + return -1; + } else if (subDetID == 1) { /// MLOT + int lay = getLayer(index); + index -= getFirstChipIndex(lay, petalcase, subDetID); // get the index of the sensing element in the layer + + const int Nhs = mNumberOfHalfStaves[lay]; + const int Nmod = mNumberOfModules[lay]; + const int Nchip = mNumberOfChips[lay]; + + int chipsPerModule = Nchip; + int chipsPerHalfStave = Nmod * chipsPerModule; + int chipsPerStave = Nhs * chipsPerHalfStave; + + int rem = index % chipsPerStave; + return rem / chipsPerHalfStave; // 0 = left, 1 = right + } + return -1; +} + +//__________________________________________________________________________ +int GeometryTGeo::getModule(int index) const +{ + int subDetID = getSubDetID(index); + int lay = getLayer(index); + int petalcase = getPetalCase(index); + + if (subDetID == 0) { /// VD + return -1; + } else if (subDetID == 1) { /// MLOT + int lay = getLayer(index); + index -= getFirstChipIndex(lay, petalcase, subDetID); // get the index of the sensing element in the layer + + const int Nhs = mNumberOfHalfStaves[lay]; + const int Nmod = mNumberOfModules[lay]; + const int Nchip = mNumberOfChips[lay]; + + if (Nhs == 2) { + int chipsPerModule = Nchip; + int chipsPerHalfStave = Nmod * chipsPerModule; + int rem = index % (Nhs * chipsPerHalfStave); + rem = rem % chipsPerHalfStave; + return rem / chipsPerModule; + } else if (Nhs == 1) { + int chipsPerModule = Nchip; + int rem = index % (Nmod * chipsPerModule); + return rem / chipsPerModule; } - return (index % mNumberOfChipsPerPetalVD[petalcase]) - mNumberOfLayersVD; } + return -1; +} - return -1; /// not found or ML/OT +//__________________________________________________________________________ +int GeometryTGeo::getChip(int index) const +{ + int subDetID = getSubDetID(index); + int lay = getLayer(index); + int petalcase = getPetalCase(index); + + if (subDetID == 0) { /// VD + return -1; + } else if (subDetID == 1) { /// MLOT + int lay = getLayer(index); + index -= getFirstChipIndex(lay, petalcase, subDetID); // get the index of the sensing element in the layer + + const int Nhs = mNumberOfHalfStaves[lay]; + const int Nmod = mNumberOfModules[lay]; + const int Nchip = mNumberOfChips[lay]; + + if (Nhs == 2) { + int chipsPerModule = Nchip; + return index % chipsPerModule; + } else if (Nhs == 1) { + int chipsPerModule = Nchip; + return index % chipsPerModule; + } + } + return -1; } //__________________________________________________________________________ -int GeometryTGeo::getChipIndex(int subDetID, int petalcase, int disk, int lay, int stave) const +unsigned short GeometryTGeo::getChipIndex(int subDetID, int petalcase, int disk, int lay, int stave, int halfstave, int mod, int chip) const { if (subDetID == 0) { // VD if (lay == -1) { // disk @@ -201,20 +322,70 @@ int GeometryTGeo::getChipIndex(int subDetID, int petalcase, int disk, int lay, i } else { // layer return getFirstChipIndex(lay, petalcase, subDetID) + lay; } - } else if (subDetID == 1) { // MLOT - return getFirstChipIndex(lay, petalcase, subDetID) + stave; + } else if (subDetID == 1) { // MLOT + const int Nhs = mNumberOfHalfStaves[lay]; // 1 or 2 + const int Nmod = mNumberOfModules[lay]; // module per half-stave (per stave if Nhs==1) + const int Nchip = mNumberOfChips[lay]; // chips per module + + if (Nhs == 2) { // staggered geometry: layer -> stave -> halfstave -> mod -> chip + int chipsPerModule = Nchip; + int chipsPerHalfStave = Nmod * chipsPerModule; + int chipsPerStave = Nhs * chipsPerHalfStave; + return getFirstChipIndex(lay, petalcase, subDetID) + stave * chipsPerStave + halfstave * chipsPerHalfStave + mod * chipsPerModule + chip; + } else if (Nhs == 1) { // turbo geometry: layer -> stave -> mod -> chip (no halfstave) + int chipsPerModule = Nchip; + int chipsPerStave = Nmod * chipsPerModule; + return getFirstChipIndex(lay, petalcase, subDetID) + stave * chipsPerStave + mod * chipsPerModule + chip; + } + } + + LOGP(warning, "Chip index not found for subDetID %d, petalcase %d, disk %d, layer %d, stave %d, halfstave %d, module %d, chip %d, returning numeric limit", subDetID, petalcase, disk, lay, stave, halfstave, mod, chip); + return std::numeric_limits::max(); // not found +} + +//__________________________________________________________________________ +unsigned short GeometryTGeo::getChipIndex(int subDetID, int volume, int lay, int stave, int halfstave, int mod, int chip) const +{ + if (subDetID == 0) { // VD + return volume; /// In the current configuration for VD, each volume is the sensor element = chip. // TODO: when the geometry naming scheme will be changed, change this method + + } else if (subDetID == 1) { // MLOT + const int Nhs = mNumberOfHalfStaves[lay]; // 1 or 2 + const int Nmod = mNumberOfModules[lay]; // module per half-stave (per stave if Nhs==1) + const int Nchip = mNumberOfChips[lay]; // chips per module + + if (Nhs == 2) { // staggered geometry: layer -> stave -> halfstave -> mod -> chip + int chipsPerModule = Nchip; + int chipsPerHalfStave = Nmod * chipsPerModule; + int chipsPerStave = Nhs * chipsPerHalfStave; + return getFirstChipIndex(lay, -1, subDetID) + stave * chipsPerStave + halfstave * chipsPerHalfStave + mod * chipsPerModule + chip; + } else if (Nhs == 1) { // turbo geometry: layer -> stave -> mod -> chip (no halfstave) + int chipsPerModule = Nchip; + int chipsPerStave = Nmod * chipsPerModule; + return getFirstChipIndex(lay, -1, subDetID) + stave * chipsPerStave + mod * chipsPerModule + chip; + } } - return -1; // not found + + LOGP(warning, "Chip index not found for subDetID %d, volume %d, layer %d, stave %d, halfstave %d, module %d, chip %d, returning numeric limit", subDetID, volume, lay, stave, halfstave, mod, chip); + return std::numeric_limits::max(); // not found } //__________________________________________________________________________ -bool GeometryTGeo::getChipID(int index, int& subDetID, int& petalcase, int& disk, int& lay, int& stave) const +bool GeometryTGeo::getChipID(int index, int& subDetID, int& petalcase, int& disk, int& lay, int& stave, int& halfstave, int& mod, int& chip) const { subDetID = getSubDetID(index); petalcase = getPetalCase(index); disk = getDisk(index); lay = getLayer(index); stave = getStave(index); + if (mNumberOfHalfStaves[lay] == 2) { + halfstave = getHalfStave(index); + } else { + halfstave = 0; // if not staggered geometry, return 0 + } + halfstave = getHalfStave(index); + mod = getModule(index); + chip = getChip(index); return kTRUE; } @@ -223,59 +394,48 @@ bool GeometryTGeo::getChipID(int index, int& subDetID, int& petalcase, int& disk TString GeometryTGeo::getMatrixPath(int index) const { - // int lay, hba, stav, sstav, mod, chipInMod; - int subDetID, petalcase, disk, lay, stave; //// TODO: add chips in a second step - getChipID(index, subDetID, petalcase, disk, lay, stave); + int subDetID, petalcase, disk, layer, stave, halfstave, mod, chip; + getChipID(index, subDetID, petalcase, disk, layer, stave, halfstave, mod, chip); - int indexRetrieved = getChipIndex(subDetID, petalcase, disk, lay, stave); + // PrintChipID(index, subDetID, petalcase, disk, layer, stave, halfstave, mod, chip); - PrintChipID(index, subDetID, petalcase, disk, lay, stave, indexRetrieved); + // TString path = "/cave_1/barrel_1/TRKV_2/TRKLayer0_1/TRKStave0_1/TRKChip0_1/TRKSensor0_1/"; /// dummy path, to be used for tests + TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getTRKVolPattern()); - // TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getTRKVolPattern()); - TString path = "/cave_1/barrel_1/TRKV_2/TRKLayer0_1/TRKStave0_1/TRKChip0_1/TRKSensor0_1/"; /// dummy path, to be replaced - - // if (wrID >= 0) { - // path += Form("%s%d_1/", getITSWrapVolPattern(), wrID); - // } - - // if (isVD) { - // path += Form("%s%d_1/", getTRKPetalPattern(), index); - - // } else { - // path += Form("%s%d_1/", getTRKLayerPattern(), index); - // } + // handling cylindrical configuration for ML and/or OT + // needed bercause of the different numbering scheme in the geometry for the cylindrical case wrt the staggered and turbo ones + if (subDetID == 1) { + if ((layer < 4 && mLayoutML == eLayout::kCylinder) || (layer > 3 && mLayoutOL == eLayout::kCylinder)) { + stave = 1; + mod = 1; + chip = 1; + } + } - // if (!mIsLayerITS3[lay]) { - // path += - // Form("%s%d_1/", getITSLayerPattern(), lay); - // if (mNumberOfHalfBarrels > 0) { - // path += Form("%s%d_%d/", getITSHalfBarrelPattern(), lay, hba); - // } - // path += - // Form("%s%d_%d/", getITSStavePattern(), lay, stav); - - // if (mNumberOfHalfStaves[lay] > 0) { - // path += Form("%s%d_%d/", getITSHalfStavePattern(), lay, sstav); - // } - // if (mNumberOfModules[lay] > 0) { - // path += Form("%s%d_%d/", getITSModulePattern(), lay, mod); - // } - // path += Form("%s%d_%d/%s%d_1", getITSChipPattern(), lay, chipInMod, getITSSensorPattern(), lay); - // } else { - // // hba = carbonform - // // stav = 0 - // // sstav = segment - // // mod = rsu - // // chipInMod = tile - // // sensor = pixelarray - // path += Form("%s_0/", getITS3LayerPattern(lay)); - // path += Form("%s_%d/", getITS3CarbonFormPattern(lay), hba); - // path += Form("%s_0/", getITS3ChipPattern(lay)); - // path += Form("%s_%d/", getITS3SegmentPattern(lay), sstav); - // path += Form("%s_%d/", getITS3RSUPattern(lay), mod); - // path += Form("%s_%d/", getITS3TilePattern(lay), chipInMod); - // path += Form("%s_0", getITS3PixelArrayPattern(lay)); - // } + // build the path + if (subDetID == 0) { // VD + if (disk >= 0) { + path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n + path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk); // PETALCASEx_DISKy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKChipPattern(), disk); // PETALCASEx_DISKy_TRKChipy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKSensorPattern(), disk); // PETALCASEx_DISKy_TRKSensory_1 + } else if (layer >= 0) { + path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n + path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer); // PETALCASEx_LAYERy_1 + // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKStavePattern(), layer); // PETALCASEx_LAYERy_TRKStavey_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKSensorPattern(), layer); // PETALCASEx_LAYERy_TRKSensory_1 + } + } else if (subDetID == 1) { // MLOT + path += Form("%s%d_1/", getTRKLayerPattern(), layer); // TRKLayerx_1 + path += Form("%s%d_%d/", getTRKStavePattern(), layer, stave); // TRKStavex_y + if (mNumberOfHalfStaves[layer] == 2) { // staggered geometry + path += Form("%s%d_%d/", getTRKHalfStavePattern(), layer, halfstave); // TRKHalfStavex_y + } + path += Form("%s%d_%d/", getTRKModulePattern(), layer, mod); // TRKModulx_y + path += Form("%s%d_%d/", getTRKChipPattern(), layer, chip); // TRKChipx_y + path += Form("%s%d_1/", getTRKSensorPattern(), layer); // TRKSensorx_1 + } return path; } @@ -286,44 +446,70 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const // Note, the if the effective sensitive layer thickness is smaller than the // total physical sensor tickness, this matrix is biased and connot be used // directly for transformation from sensor frame to global one. - // // Therefore we need to add a shift + auto path = getMatrixPath(index); static TGeoHMatrix matTmp; - gGeoManager->PushPath(); + gGeoManager->PushPath(); // Preserve the modeler state. - // if (!gGeoManager->cd(path.Data())) { - // gGeoManager->PopPath(); - // LOG(error) << "Error in cd-ing to " << path.Data(); - // return nullptr; - // } // end if !gGeoManager + if (!gGeoManager->cd(path.Data())) { + gGeoManager->PopPath(); + LOG(error) << "Error in cd-ing to " << path.Data(); + return nullptr; + } // end if !gGeoManager matTmp = *gGeoManager->GetCurrentMatrix(); // matrix may change after cd // RSS - // printf("%d/%d/%d %s\n", lay, stav, detInSta, path.Data()); // matTmp.Print(); // Restore the modeler state. gGeoManager->PopPath(); static int chipInGlo{0}; + /// TODO: // 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; - // 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 + // in the VD case this will be accounted by specialized functions during the clusterization (following what it is done for ITS3) + // this can be done once the right sensor thickness is in place in the geometry + // double delta = 0.; + // if (getSubDetID(index) == 1){ /// ML/OT + // delta = Segmentation::SensorLayerThicknessVD - Segmentation::SiliconTickness; + // static TGeoTranslation tra(0., 0.5 * delta, 0.); // matTmp *= tra; - // #endif + // } + // std::cout<<"-----"<GetVolume(getTRKVolPattern()); + if (!trkV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; + } - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + // Loop on all TRKV nodes, count PETAL assemblies and their contents + TObjArray* nodes = trkV->GetNodes(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; } - LOG(info) << "Volume name: " << getTRKVolPattern(); - // Loop on all TRKV nodes, count Layer volumes by checking names - TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + LOGP(info, "Searching for petal assemblies in {} (pattern: {})", + getTRKVolPattern(), getTRKPetalAssemblyPattern()); + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); - if (strstr(name, getTRKPetalPattern()) != nullptr && (strstr(name, getTRKPetalLayerPattern()) != nullptr || strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfParts++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + if (strstr(name, getTRKPetalAssemblyPattern()) != nullptr) { + numberOfPetals++; + LOGP(info, "Found petal assembly: {}", name); + + // Get petal volume and its nodes for debugging + TGeoVolume* petalVol = nd->GetVolume(); + if (petalVol) { + TObjArray* petalNodes = petalVol->GetNodes(); + if (petalNodes) { + LOGP(debug, "Petal {} contains {} child nodes", name, petalNodes->GetEntriesFast()); + // Print all nodes in this petal + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + LOGP(debug, " Node {}: {}", k, petalNode->GetName()); + } + } else { + LOGP(warning, "Petal {} has no child nodes", name); + } + } else { + LOGP(warning, "Petal {} has no volume", name); } } } - return numberOfParts; + + if (numberOfPetals == 0) { + LOGP(warning, "No petal assemblies found in geometry"); + } else { + LOGP(info, "Found {} petal assemblies", numberOfPetals); + } + + return numberOfPetals; } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfDisksVD() const +int GeometryTGeo::extractNumberOfActivePartsVD() const { - // The number of disks returned here is 6 - int numberOfDisks = 0; - + // The number of active parts returned here is 36 = 4 petals * (3 layers + 6 disks) + int numberOfParts = 0; TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - LOG(info) << "Volume name: " << getTRKVolPattern(); - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal to count its active parts TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } + + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); + if (strstr(name, getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && (strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfDisks++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + petalFound = true; + LOGP(info, "Counting active parts in petal: {}", name); + + // Found a petal, count its layers and disks + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", name); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", name); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + const char* nodeName = petalNode->GetName(); + + if (strstr(nodeName, getTRKPetalLayerPattern()) != nullptr || + strstr(nodeName, getTRKPetalDiskPattern()) != nullptr) { + numberOfParts++; + LOGP(debug, "Found active part in {}: {}", name, nodeName); } } + // We only need to check one petal as they're identical + break; } - return numberOfDisks; + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + return 0; + } + + if (numberOfParts == 0) { + LOGP(warning, "No active parts (layers/disks) found in petal"); + return 0; + } + + // Multiply by number of petals since all petals are identical + int totalParts = numberOfParts * mNumberOfPetalsVD; + LOGP(info, "Total number of active parts: {} ({}*{})", + totalParts, numberOfParts, mNumberOfPetalsVD); + return totalParts; } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfPetalsVD() const +int GeometryTGeo::extractNumberOfDisksVD() const { - // The number of petals returned here is 4 = number of petals - int numberOfChips = 0; - + // Count disks in the first petal (all petals are identical) + int numberOfDisks = 0; TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - LOG(info) << "Volume name: " << getTRKVolPattern(); - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); - const char* name = nd->GetName(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } - if (strstr(name, getTRKPetalPattern()) != nullptr && (strstr(name, Form("%s%s", getTRKPetalLayerPattern(), "0")) != nullptr)) { - numberOfChips++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); + if (strstr(nd->GetName(), getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } + + petalFound = true; + LOGP(info, "Counting disks in petal: {}", nd->GetName()); + + // Count disks in this petal + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", nd->GetName()); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", nd->GetName()); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + if (strstr(petalNode->GetName(), getTRKPetalDiskPattern()) != nullptr) { + numberOfDisks++; + LOGP(info, "Found disk in {} : {}", nd->GetName(), petalNode->GetName()); } } + // One petal is enough + break; } - return numberOfChips; + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfDisks == 0) { + LOGP(warning, "No disks found in VD geometry"); + } + + return numberOfDisks; } //__________________________________________________________________________ int GeometryTGeo::extractNumberOfLayersVD() const { - // The number of layers returned here is 3 + // Count layers in the first petal (all petals are identical) int numberOfLayers = 0; - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - LOG(info) << "Volume name: " << getTRKVolPattern(); - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); - const char* name = nd->GetName(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && strstr(name, getTRKPetalLayerPattern()) != nullptr) { - numberOfLayers++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); + if (strstr(nd->GetName(), getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } + + petalFound = true; + LOGP(info, "Counting layers in petal: {}", nd->GetName()); + + // Count layers in this petal + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", nd->GetName()); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", nd->GetName()); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + if (strstr(petalNode->GetName(), getTRKPetalLayerPattern()) != nullptr) { + numberOfLayers++; + LOGP(info, "Found layer in {} : {}", nd->GetName(), petalNode->GetName()); } } + // One petal is enough + break; + } + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); } + + if (numberOfLayers == 0) { + LOGP(warning, "No layers found in VD geometry"); + } + return numberOfLayers; } @@ -555,28 +904,82 @@ int GeometryTGeo::extractNumberOfChipsPerPetalVD() const { // The number of chips per petal returned here is 9 for each layer = number of layers + number of quarters of disks per petal int numberOfChips = 0; - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - LOG(info) << "Volume name: " << getTRKVolPattern(); - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal assembly TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } + + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); + if (strstr(name, getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && (strstr(name, getTRKPetalLayerPattern()) != nullptr || strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfChips++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + petalFound = true; + LOGP(info, "Counting chips in petal: {}", name); + + // Found a petal, count sensors in its layers and disks + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", name); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", name); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + const char* nodeName = petalNode->GetName(); + TGeoVolume* vol = petalNode->GetVolume(); + + if (!vol) { + LOGP(debug, "Node {} has no volume", nodeName); + continue; + } + + // Look for sensors in this volume + TObjArray* subNodes = vol->GetNodes(); + if (!subNodes) { + LOGP(debug, "Node {} has no sub-nodes", nodeName); + continue; + } + + for (int i = 0; i < subNodes->GetEntriesFast(); i++) { + auto* subNode = dynamic_cast(subNodes->At(i)); + if (strstr(subNode->GetName(), getTRKChipPattern()) != nullptr) { + numberOfChips++; + LOGP(debug, "Found chip in {}: {}", nodeName, subNode->GetName()); + } } } + // We only need one petal + break; + } + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfChips == 0) { + LOGP(warning, "No chips/sensors found in VD petal"); } + + LOGP(info, "Number of chips per petal: {}", numberOfChips); return numberOfChips; } @@ -610,7 +1013,91 @@ int GeometryTGeo::extractNumberOfStavesMLOT(int lay) const } //__________________________________________________________________________ -void GeometryTGeo::PrintChipID(int index, int subDetID, int petalcase, int disk, int lay, int stave, int indexRetrieved) const +int GeometryTGeo::extractNumberOfHalfStavesMLOT(int lay) const +{ + int numberOfHalfStaves = 0; + + std::string staveName = Form("%s%d", getTRKStavePattern(), lay); + TGeoVolume* staveV = gGeoManager->GetVolume(staveName.c_str()); + + if (staveV == nullptr) { + LOG(fatal) << getName() << " volume " << getTRKStavePattern() << " is not in the geometry"; + } + + // Loop on all layV nodes, count Layer volumes by checking names + TObjArray* nodes = staveV->GetNodes(); + // std::cout << "Printing nodes for layer " << lay << std::endl; + // nodes->Print(); + int nNodes = nodes->GetEntriesFast(); + + for (int j = 0; j < nNodes; j++) { + auto nd = dynamic_cast(nodes->At(j)); /// layer node + const char* name = nd->GetName(); + if (strstr(name, getTRKHalfStavePattern()) != nullptr) { + numberOfHalfStaves++; + } + } + + if (numberOfHalfStaves == 0) { + numberOfHalfStaves = 1; /// in case of turbo geometry, there is no half stave volume, but only stave volume + } + return numberOfHalfStaves; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfModulesMLOT(int lay) const +{ + int numberOfModules = 0; + + std::string staveName = Form("%s%d", (mNumberOfHalfStaves[lay] == 2 ? getTRKHalfStavePattern() : getTRKStavePattern()), lay); + TGeoVolume* staveV = gGeoManager->GetVolume(staveName.c_str()); + + if (staveV == nullptr) { + LOG(fatal) << getName() << " volume " << (mNumberOfHalfStaves[lay] == 2 ? getTRKHalfStavePattern() : getTRKStavePattern()) << " is not in the geometry"; + } + + // Loop on all staveV nodes, count Module volumes by checking names + TObjArray* nodes = staveV->GetNodes(); + int nNodes = nodes->GetEntriesFast(); + + for (int j = 0; j < nNodes; j++) { + auto nd = dynamic_cast(nodes->At(j)); /// stave node + const char* name = nd->GetName(); + if (strstr(name, getTRKModulePattern()) != nullptr) { + numberOfModules++; + } + } + return numberOfModules; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfChipsMLOT(int lay) const +{ + int numberOfChips = 0; + + std::string moduleName = Form("%s%d", getTRKModulePattern(), lay); + TGeoVolume* moduleV = gGeoManager->GetVolume(moduleName.c_str()); + + if (moduleV == nullptr) { + LOG(fatal) << getName() << " volume " << getTRKModulePattern() << " is not in the geometry"; + } + + // Loop on all moduleV nodes, count Chip volumes by checking names + TObjArray* nodes = moduleV->GetNodes(); + int nNodes = nodes->GetEntriesFast(); + + for (int j = 0; j < nNodes; j++) { + auto nd = dynamic_cast(nodes->At(j)); /// module node + const char* name = nd->GetName(); + if (strstr(name, getTRKChipPattern()) != nullptr) { + numberOfChips++; + } + } + return numberOfChips; +} + +//__________________________________________________________________________ +void GeometryTGeo::PrintChipID(int index, int subDetID, int petalcase, int disk, int lay, int stave, int halfstave, int mod, int chip) const { std::cout << "\nindex = " << index << std::endl; std::cout << "subDetID = " << subDetID << std::endl; @@ -619,7 +1106,9 @@ void GeometryTGeo::PrintChipID(int index, int subDetID, int petalcase, int disk, std::cout << "disk = " << disk << std::endl; std::cout << "first chip index = " << getFirstChipIndex(lay, petalcase, subDetID) << std::endl; std::cout << "stave = " << stave << std::endl; - std::cout << "chck index Retrieved = " << indexRetrieved << std::endl; + std::cout << "halfstave = " << halfstave << std::endl; + std::cout << "module = " << mod << std::endl; + std::cout << "chip = " << chip << std::endl; } //__________________________________________________________________________ @@ -641,11 +1130,23 @@ void GeometryTGeo::Print(Option_t*) const for (int i = 0; i < mNumberOfPetalsVD; i++) { LOGF(info, "%d", mNumberOfChipsPerPetalVD[i]); } - LOGF(info, "Number of staves per layer MLOT: "); + LOGF(info, "Number of staves and half staves per layer MLOT: "); for (int i = 0; i < mNumberOfLayersMLOT; i++) { std::string mlot = ""; - mlot = (i < 5) ? "ML" : "OT"; - LOGF(info, "Layer: %d, %s, %d staves", i, mlot.c_str(), mNumberOfStaves[i]); + mlot = (i < 4) ? "ML" : "OT"; + LOGF(info, "Layer: %d, %s, %d staves, %d half staves per stave", i, mlot.c_str(), mNumberOfStaves[i], mNumberOfHalfStaves[i]); + } + LOGF(info, "Number of modules per stave (half stave) in each ML(OT) layer: "); + for (int i = 0; i < mNumberOfLayersMLOT; i++) { + LOGF(info, "%d", mNumberOfModules[i]); + } + LOGF(info, "Number of chips per module MLOT: "); + for (int i = 0; i < mNumberOfLayersMLOT; i++) { + LOGF(info, "%d", mNumberOfChips[i]); + } + LOGF(info, "Number of chips per layer MLOT: "); + for (int i = 0; i < mNumberOfLayersMLOT; i++) { + LOGF(info, "%d", mNumberOfChipsPerLayerMLOT[i]); } LOGF(info, "Total number of chips: %d", getNumberOfChips()); @@ -667,5 +1168,87 @@ void GeometryTGeo::Print(Option_t*) const std::cout << "]" << std::endl; } +//__________________________________________________________________________ +int GeometryTGeo::getBarrelLayer(int chipID) const +{ + // for barrel layers only, + // so it would be consistent with number of layers i.e. from 0 to 10, + // starting from VD0 to OT10; + // skip the disks; + + int subDetID = getSubDetID(chipID); + int subLayerID = getLayer(chipID); + + if (subDetID < 0 || subDetID > 1) { + LOG(error) << "getBarrelLayer(): Invalid subDetID for barrel: " << subDetID + << ". Expected values are 0 or 1."; + return -1; + } + + if (subLayerID < 0 || subLayerID > 7) { + LOG(error) << "getBarrelLayer(): Invalid subLayerID for barrel: " << subDetID + << ". Expected values are between 0 and 7."; + return -1; + } + + const int baseOffsets[] = {0, 3}; + + return baseOffsets[subDetID] + subLayerID; +} + +//__________________________________________________________________________ +void GeometryTGeo::extractSensorXAlphaMLOT(int chipID, float& x, float& alp) +{ + // works for ML and OT only, a.k.a flat sensors !!! + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + double xp{0}, yp{0}; + + if (getSubDetID(chipID) == 0) { + + LOG(error) << "extractSensorXAlphaMLOT(): VD layers are not supported yet! chipID = " << chipID; + return; + + } else { // flat sensors, ML and OT + const TGeoHMatrix* matL2G = extractMatrixSensor(chipID); + 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; + } + + alp = std::atan2(yp, xp); + x = std::hypot(xp, yp); + o2::math_utils::bringTo02Pi(alp); + + /// TODO: + // once the VD segmentation is done, VD should be added +} + +//__________________________________________________________________________ +TGeoHMatrix& GeometryTGeo::createT2LMatrixMLOT(int chipID) +{ + // works only for ML & OT + // for VD is yet to be implemented once we have more refined geometry + if (getSubDetID(chipID) == 0) { + + LOG(error) << "createT2LMatrixMLOT(): VD layers are not supported yet! chipID = " << chipID + << "returning dummy values! "; + static TGeoHMatrix dummy; + return dummy; + + } else { + static TGeoHMatrix t2l; + t2l.Clear(); + float alpha = getSensorRefAlphaMLOT(chipID); + t2l.RotateZ(alpha * TMath::RadToDeg()); + const TGeoHMatrix* matL2G = extractMatrixSensor(chipID); + const TGeoHMatrix& matL2Gi = matL2G->Inverse(); + t2l.MultiplyLeft(&matL2Gi); + return t2l; + } +} + } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/SegmentationChip.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/SegmentationChip.cxx new file mode 100644 index 0000000000000..26e76530597d7 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/SegmentationChip.cxx @@ -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. + +/// \file SegmentationChip.cxx +/// \brief Implementation of the SegmentationChip class + +#include "TRKBase/SegmentationChip.h" +#include + +using namespace o2::trk; + +// void SegmentationChip::print() +// { +// printf("++++++++++ VD ++++++++++"); +// printf("Pixel size: %.2f (along %d rows) %.2f (along %d columns) microns\n", PitchRowVD * 1e4, -999, PitchColVD * 1e4, NColsVD); +// printf("++++++++++ ML ++++++++++"); +// printf("Pixel size: %.2f (along %d rows) %.2f (along %d columns) microns\n", PitchRowML * 1e4, -999, PitchColML * 1e4, NColsML); + +// } \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h index f29dcd302537d..eee9a23eaf5e7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h @@ -15,8 +15,10 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::TRKBaseParam> + ; + #pragma link C++ class o2::trk::GeometryTGeo + #pragma link C++ class o2::trk::TRKBaseParam + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::TRKBaseParam> + ; +#pragma link C++ class o2::trk::SegmentationChip + ; #endif \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt new file mode 100644 index 0000000000000..9a2194afd3999 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(test) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt new file mode 100644 index 0000000000000..d9908bbfeb1e5 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -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. + +o2_add_test_root_macro(CheckDigits.C + PUBLIC_LINK_LIBRARIES O2::ITSMFTBase + O2::ITSMFTSimulation + O2::TRKBase + O2::TRKSimulation + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + O2::Steer + LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(CheckTracksCA.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::ITStracking + O2::SimulationDataFormat + O2::DetectorsBase + O2::TRKBase + O2::TRKSimulation + O2::Steer + LABELS trk COMPILE_ONLY) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C new file mode 100644 index 0000000000000..5d60592a96f41 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C @@ -0,0 +1,387 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckDigits.C +/// \brief Simple macro to check TRK digits + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include + +#include "TRKBase/SegmentationChip.h" +#include "TRKBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/Digit.h" +#include "ITSMFTSimulation/Hit.h" +#include "MathUtils/Utils.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsITSMFT/ROFRecord.h" + +#endif + +#define ENABLE_UPGRADES + +void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = "o2sim_HitsTRK.root", std::string inputGeom = "", std::string paramfile = "o2sim_par.root") +{ + + using namespace o2::base; + using namespace o2::trk; + + using o2::itsmft::Digit; + using o2::itsmft::Hit; + + using o2::trk::SegmentationChip; + + TFile* f = TFile::Open("CheckDigits.root", "recreate"); + + TNtuple* nt = new TNtuple("ntd", "digit ntuple", "id:x:y:z:rowD:colD:rowH:colH:xlH:zlH:xlcH:zlcH:dx:dz"); + TNtuple* nt2 = new TNtuple("ntd2", "digit ntuple", "id:z:dxH:dzH"); /// maximum number of elements in a tuple = 15: doing a new tuple to store more variables + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto* gman = o2::trk::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + SegmentationChip seg; + // seg.Print(); + + // Hits + TFile* hitFile = TFile::Open(hitfile.data()); + TTree* hitTree = (TTree*)hitFile->Get("o2sim"); + int nevH = hitTree->GetEntries(); // hits are stored as one event per entry + std::vector*> hitArray(nevH, nullptr); + + std::vector> mc2hitVec(nevH); + + // Digits + TFile* digFile = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)digFile->Get("o2sim"); + + std::vector* digArr = nullptr; + digTree->SetBranchAddress("TRKDigit", &digArr); + + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; + digTree->SetBranchAddress("TRKDigitMCTruth", &plabels); + + // Get Read Out Frame arrays + std::vector* ROFRecordArrray = nullptr; + digTree->SetBranchAddress("TRKDigitROF", &ROFRecordArrray); + std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + + std::vector* MC2ROFRecordArrray = nullptr; + digTree->SetBranchAddress("TRKDigitMC2ROF", &MC2ROFRecordArrray); + std::vector& MC2ROFRecordArrrayRef = *MC2ROFRecordArrray; + + digTree->GetEntry(0); + + int nROFRec = (int)ROFRecordArrrayRef.size(); + std::vector mcEvMin(nROFRec, hitTree->GetEntries()); + std::vector mcEvMax(nROFRec, -1); + o2::dataformats::ConstMCTruthContainer labels; + plabels->copyandflatten(labels); + delete plabels; + + // >> build min and max MC events used by each ROF + for (int imc = MC2ROFRecordArrrayRef.size(); imc--;) { + const auto& mc2rof = MC2ROFRecordArrrayRef[imc]; + // printf("MCRecord: "); + // mc2rof.print(); + + if (mc2rof.rofRecordID < 0) { + continue; // this MC event did not contribute to any ROF + } + + for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + + int irof = mc2rof.rofRecordID + irfd; + + if (irof >= nROFRec) { + LOG(error) << "ROF=" << irof << " from MC2ROF record is >= N ROFs=" << nROFRec; + } + if (mcEvMin[irof] > imc) { + mcEvMin[irof] = imc; + } + if (mcEvMax[irof] < imc) { + mcEvMax[irof] = imc; + } + } + } // << build min and max MC events used by each ROF + + unsigned int rofIndex = 0; + unsigned int rofNEntries = 0; + + // LOOP on : ROFRecord array + for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { + + rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); + rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); + + // >> read and map MC events contributing to this ROF + for (int im = mcEvMin[iROF]; im <= mcEvMax[iROF]; im++) { + + if (!hitArray[im]) { + + hitTree->SetBranchAddress("TRKHit", &hitArray[im]); + hitTree->GetEntry(im); + + auto& mc2hit = mc2hitVec[im]; + + for (int ih = hitArray[im]->size(); ih--;) { + + const auto& hit = (*hitArray[im])[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + + // LOOP on : digits array + for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { + // if (iDigit % 10000 != 0) /// looking only at a small sample + // continue; + + if (iDigit % 1000 == 0) + std::cout << "Reading digit " << iDigit << " / " << digArr->size() << std::endl; + + Int_t ix = (*digArr)[iDigit].getRow(), iz = (*digArr)[iDigit].getColumn(); + Int_t iDetID = (*digArr)[iDigit].getChipIndex(); + Int_t layer = gman->getLayer(iDetID); + Int_t disk = gman->getDisk(iDetID); + Int_t subDetID = gman->getSubDetID(iDetID); + Int_t petalCase = gman->getPetalCase(iDetID); + Int_t stave = gman->getStave(iDetID); + Int_t halfstave = gman->getHalfStave(iDetID); + + Float_t x = 0.f, y = 0.f, z = 0.f; + Float_t x_flat = 0.f, z_flat = 0.f; + + if (disk != -1) { + continue; // skip disks for the moment + } + + if (subDetID != 0) { + seg.detectorToLocal(ix, iz, x, z, subDetID, layer, disk); + } else if (subDetID == 0) { + seg.detectorToLocal(ix, iz, x_flat, z_flat, subDetID, layer, disk); + o2::math_utils::Vector2D xyCurved = seg.flatToCurved(layer, x_flat, 0.); + x = xyCurved.X(); + y = xyCurved.Y(); + z = z_flat; + } + + o2::math_utils::Point3D locD(x, y, z); // local Digit curved + o2::math_utils::Point3D locDF(-1, -1, -1); // local Digit flat + + Int_t chipID = (*digArr)[iDigit].getChipIndex(); + auto lab = (labels.getLabels(iDigit))[0]; + + int trID = lab.getTrackID(); + + if (!lab.isValid()) { // not a noise + continue; + } + + const auto gloD = gman->getMatrixL2G(chipID)(locD); // convert to global + + std::unordered_map* mc2hit = &mc2hitVec[lab.getEventID()]; + + // get MC info + uint64_t key = (uint64_t(trID) << 32) + chipID; + auto hitEntry = mc2hit->find(key); + + if (hitEntry == mc2hit->end()) { + + LOG(error) << "Failed to find MC hit entry for Tr" << trID << " chipID" << chipID; + continue; + } + + ////// HITS + Hit& hit = (*hitArray[lab.getEventID()])[hitEntry->second]; + + auto xyzLocE = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local + auto xyzLocS = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); + + o2::math_utils::Vector3D locH; /// Hit, average between start and end pos + locH.SetCoordinates(0.5f * (xyzLocE.X() + xyzLocS.X()), 0.5f * (xyzLocE.Y() + xyzLocS.Y()), 0.5f * (xyzLocE.Z() + xyzLocS.Z())); + o2::math_utils::Vector3D locHS; /// Hit, start pos + locHS.SetCoordinates(xyzLocS.X(), xyzLocS.Y(), xyzLocS.Z()); + o2::math_utils::Vector3D locHE; /// Hit, end pos + locHE.SetCoordinates(xyzLocE.X(), xyzLocE.Y(), xyzLocE.Z()); + o2::math_utils::Vector3D locHF; + + int row = 0, col = 0; + float xlc = 0., zlc = 0.; + + if (subDetID == 0) { + Float_t x_flat = 0.f, y_flat = 0.f; + o2::math_utils::Vector2D xyFlatH = seg.curvedToFlat(layer, locH.X(), locH.Y()); + o2::math_utils::Vector2D xyFlatD = seg.curvedToFlat(layer, locD.X(), locD.Y()); + locDF.SetCoordinates(xyFlatD.X(), xyFlatD.Y(), locD.Z()); + locHF.SetCoordinates(xyFlatH.X(), xyFlatH.Y(), locH.Z()); + seg.localToDetector(locHF.X(), locHF.Z(), row, col, subDetID, layer, disk); + } + + else { + seg.localToDetector(locH.X(), locH.Z(), row, col, subDetID, layer, disk); + } + + seg.detectorToLocal(row, col, xlc, zlc, subDetID, layer, disk); + + if (subDetID == 0) { + nt->Fill(chipID, /// detector ID + gloD.X(), gloD.Y(), gloD.Z(), /// global position retrieved from the digit: digit (row, col) ->local position -> global potision + ix, iz, /// row and column of the digit + row, col, /// row and col retrieved from the hit: hit global position -> hit local position -> detector position (row, col) + locH.X(), locH.Z(), /// x and z of the hit in the local reference frame: hit global position -> hit local position + xlc, zlc, /// x and z of the hit in the local frame: hit global position -> hit local position -> detector position (row, col) -> local position + locHF.X() - locDF.X(), locHF.Z() - locDF.Z()); /// difference in x and z between the hit and the digit in the local frame + + nt2->Fill(chipID, gloD.Z(), locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// differences between local hit start and hit end positions + } else { + + nt->Fill(chipID, /// detector ID + gloD.X(), gloD.Y(), gloD.Z(), /// global position retrieved from the digit: digit (row, col) ->local position -> global potision + ix, iz, /// row and column of the digit + row, col, /// row and col retrieved from the hit: hit global position -> hit local position -> detector position (row, col) + locH.X(), locH.Z(), /// x and z of the hit in the local reference frame: hit global position -> hit local position + xlc, zlc, /// x and z of the hit in the local frame: hit global position -> hit local position -> detector position (row, col) -> local position + locH.X() - locD.X(), locH.Z() - locD.Z()); /// difference in x and z between the hit and the digit in the local frame + // locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// difference in x and z between the hit and the digit in the local frame + nt2->Fill(chipID, gloD.Z(), locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// differences between local hit start and hit end positions + } + + } // end loop on digits array + + } // end loop on ROFRecords array + + // digit maps in the xy and yz planes + auto canvXY = new TCanvas("canvXY", "", 1600, 2400); + canvXY->Divide(2, 3); + canvXY->cd(1); + nt->Draw("y:x >>h_y_vs_x_VD(1000, -3, 3, 1000, -3, 3)", "id < 36 ", "colz"); + canvXY->cd(2); + nt->Draw("y:z>>h_y_vs_z_VD(1000, -26, 26, 1000, -3, 3)", "id < 36 ", "colz"); + canvXY->cd(3); + nt->Draw("y:x>>h_y_vs_x_ML(1000, -25, 25, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + canvXY->cd(4); + nt->Draw("y:z>>h_y_vs_z_ML(1000, -70, 70, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + canvXY->cd(5); + nt->Draw("y:x>>h_y_vs_x_OT(1000, -85, 85, 1000, -85, 85)", "id >= 106 ", "colz"); + canvXY->cd(6); + nt->Draw("y:z>>h_y_vs_z_OT(1000, -85, 85, 1000, -130, 130)", "id >= 106 ", "colz"); + canvXY->SaveAs("trkdigits_y_vs_x_vs_z.pdf"); + + // z distributions + auto canvZ = new TCanvas("canvZ", "", 800, 2400); + canvZ->Divide(1, 3); + canvZ->cd(1); + nt->Draw("z>>h_z_VD(500, -26, 26)", "id < 36 "); + canvZ->cd(2); + nt->Draw("z>>h_z_ML(500, -70, 70)", "id >= 36 && id < 106 "); + canvZ->cd(3); + nt->Draw("z>>h_z_OT(500, -85, 85)", "id >= 106 "); + canvZ->SaveAs("trkdigits_z.pdf"); + + // dz distributions (difference between local position of digits and hits in x and z) + auto canvdZ = new TCanvas("canvdZ", "", 800, 2400); + canvdZ->Divide(1, 3); + canvdZ->cd(1); + nt->Draw("dz>>h_dz_VD(500, -0.05, 0.05)", "id < 36 "); + canvdZ->cd(2); + nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 36 && id < 106 "); + canvdZ->cd(3); + nt->Draw("dz>>h_dz_OT(500, -0.05, 0.05)", "id >= 106 "); + canvdZ->SaveAs("trkdigits_dz.pdf"); + + // distributions of differences between local positions of digits and hits in x and z + auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 2400); + canvdXdZ->Divide(2, 3); + canvdXdZ->cd(1); + nt->Draw("dx:dz>>h_dx_vs_dz_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36", "colz"); + auto h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_VD"); + LOG(info) << "dx, dz"; + Info("VD", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(2); + nt->Draw("dx:dz>>h_dx_vs_dz_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_VD_z"); + Info("VD |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(3); + nt->Draw("dx:dz>>h_dx_vs_dz_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ML"); + Info("ML", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(4); + nt->Draw("dx:dz>>h_dx_vs_dz_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ML_z"); + Info("ML |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->SaveAs("trkdigits_dx_vs_dz.pdf"); + canvdXdZ->cd(5); + nt->Draw("dx:dz>>h_dx_vs_dz_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT"); + Info("OT", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(6); + nt->Draw("dx:dz>>h_dx_vs_dz_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT_z"); + Info("OT |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->SaveAs("trkdigits_dx_vs_dz.pdf"); + + // distribution of differences between hit start and hit end in local coordinates + auto canvdXdZHit = new TCanvas("canvdXdZHit", "", 1600, 2400); + canvdXdZHit->Divide(2, 3); + canvdXdZHit->cd(1); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36", "colz"); + LOG(info) << "dxH, dzH"; + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_VD"); + Info("VD", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(2); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_VD_z"); + Info("VD |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(3); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ML"); + Info("ML", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(4); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ML_z"); + Info("ML |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->SaveAs("trkdigits_dxH_vs_dzH.pdf"); + canvdXdZHit->cd(5); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OT"); + Info("OT", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(6); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OT_z"); + Info("OT |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->SaveAs("trkdigits_dxH_vs_dzH.pdf"); + + f->Write(); + f->Close(); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C new file mode 100644 index 0000000000000..ae75616b7719c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C @@ -0,0 +1,345 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckTracksCA.C +/// \brief Quality assurance macro for TRK tracking + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "DetectorsBase/GeometryManager.h" + +#endif + +using namespace std; +using namespace o2; + +/// Structure to track particle hit information +struct ParticleHitInfo { + std::bitset<11> layerHits; ///< Which layers have hits (11 layers for TRK) + int nHits = 0; ///< Total number of hits + float pt = 0.0f; ///< Particle pT + + void addHit(int layer) + { + if (!layerHits[layer]) { + layerHits[layer] = true; + nHits++; + } + } + + bool hasConsecutiveLayers(int nConsecutive) const + { + for (int startLayer = 0; startLayer <= 11 - nConsecutive; ++startLayer) { + bool allSet = true; + for (int i = 0; i < nConsecutive; ++i) { + if (!layerHits[startLayer + i]) { + allSet = false; + break; + } + } + if (allSet) { + return true; + } + } + return false; + } +}; + +void CheckTracksCA(std::string tracfile = "o2trac_trk.root", + std::string kinefile = "o2sim_Kine.root", + std::string hitsfile = "o2sim_HitsTRK.root", + std::string outputfile = "trk_qa_output.root") +{ + gStyle->SetOptStat(0); + + std::cout << "=== Starting TRK Track Quality Assurance ===" << std::endl; + std::cout << "Input files:" << std::endl; + std::cout << " Tracks: " << tracfile << std::endl; + std::cout << " Kinematics: " << kinefile << std::endl; + std::cout << " Hits: " << hitsfile << std::endl; + std::cout << " Output: " << outputfile << std::endl; + std::cout << std::endl; + + // MC kinematics reader + o2::steer::MCKinematicsReader kineReader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + const int nEvents = kineReader.getNEvents(0); + std::cout << "Number of MC events: " << nEvents << std::endl; + + // Open hits file to count hits per particle per layer + TFile* hitsFile = TFile::Open(hitsfile.c_str(), "READ"); + if (!hitsFile || hitsFile->IsZombie()) { + std::cerr << "ERROR: Cannot open hits file: " << hitsfile << std::endl; + return; + } + TTree* hitsTree = hitsFile->Get("o2sim"); + if (!hitsTree) { + std::cerr << "ERROR: Cannot find o2sim tree in hits file" << std::endl; + return; + } + + // Open reconstructed tracks file + TFile* tracFile = TFile::Open(tracfile.c_str(), "READ"); + if (!tracFile || tracFile->IsZombie()) { + std::cerr << "ERROR: Cannot open tracks file: " << tracfile << std::endl; + return; + } + TTree* recTree = tracFile->Get("o2sim"); + if (!recTree) { + std::cerr << "ERROR: Cannot find o2sim tree in tracks file" << std::endl; + return; + } + + // Reconstructed tracks and labels + std::vector* recTracks = nullptr; + std::vector* trkLabels = nullptr; + recTree->SetBranchAddress("TRKTrack", &recTracks); + recTree->SetBranchAddress("TRKTrackMCTruth", &trkLabels); + + std::cout << "Reading tracks from tree..." << std::endl; + + // Analyze hits tree to count hits per particle per layer + std::cout << "Analyzing hits from tree..." << std::endl; + std::unordered_map particleHitMap; + + // Load geometry for layer determination + o2::base::GeometryManager::loadGeometry(); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + // Array to map detector to starting layer + constexpr std::array startLayer{0, 3}; + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + Long64_t nHitsEntries = hitsTree->GetEntries(); + std::cout << "Processing " << nHitsEntries << " hit entries..." << std::endl; + + for (Long64_t iEntry = 0; iEntry < nHitsEntries; ++iEntry) { + hitsTree->GetEntry(iEntry); + + for (const auto& hit : *trkHit) { + // Skip disk hits (only barrel) + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; + } + + // Determine layer + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + // Create label for this particle + o2::MCCompLabel label(hit.GetTrackID(), static_cast(iEntry), 0); + + // Add hit to particle's hit map + particleHitMap[label].addHit(layer); + } + } + + std::cout << "Found " << particleHitMap.size() << " unique particles with hits" << std::endl; + + // Store particle info and fill generated histograms + std::unordered_map particlePtMap; + + // Create histograms + constexpr int nLayers = 11; + constexpr int nb = 100; + double xbins[nb + 1], ptcutl = 0.05, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) + xbins[i] = ptcutl * std::exp(i * a); + + TH1D genParticlePtHist("genParticlePt", "Generated Particle p_{T} (All Layers); #it{p}_{T} (GeV/#it{c}); Counts", nb, xbins); + TH1D genParticlePt7LayersHist("genParticlePt7Layers", "Generated Particle p_{T} with hits in at least 7 consecutive layers; #it{p}_{T} (GeV/#it{c}); Counts", nb, xbins); + TH1D goodTracks("goodTracks", "Good Tracks; p_{T} (GeV/c); Counts", nb, xbins); + TH1D fakeTracks("fakeTracks", "Fake Tracks; p_{T} (GeV/c); Counts", nb, xbins); + + std::array goodTracksMatching, fakeTracksMatching; + for (int i = 0; i < 5; ++i) { + goodTracksMatching[i] = TH1D(Form("goodTracksMatching_%dLayers", i + 7), + Form("Good Tracks with %d layer hits; p_{T} (GeV/c); Counts", i + 7), + nb, xbins); + fakeTracksMatching[i] = TH1D(Form("fakeTracksMatching_%dLayers", i + 7), + Form("Fake Tracks with %d layer hits; p_{T} (GeV/c); Counts", i + 7), + nb, xbins); + } + + TH1D numberOfClustersPerTrack("numberOfClustersPerTrack", + "Number of clusters per track; N_{clusters}; Counts", + 12, -0.5, 11.5); + + // First pass: identify particles with full hit coverage from kinematics + std::cout << "Analyzing MC particles..." << std::endl; + for (int iEvent = 0; iEvent < nEvents; ++iEvent) { + const auto& mcTracks = kineReader.getTracks(iEvent); + for (size_t iTrack = 0; iTrack < mcTracks.size(); ++iTrack) { + const auto& mcTrack = mcTracks[iTrack]; + if (!mcTrack.isPrimary()) { + continue; + } + + // Create label for this particle + o2::MCCompLabel label(iTrack, iEvent, 0); + float pt = mcTrack.GetPt(); + + // Store particle info + particlePtMap[label] = pt; + + // Check if this particle has hits + auto hitIt = particleHitMap.find(label); + if (hitIt != particleHitMap.end()) { + // Store pT in hit info + hitIt->second.pt = pt; + + // Fill histogram for particles with hits in all 11 layers + if (hitIt->second.nHits == 11) { + genParticlePtHist.Fill(pt); + } + + // Fill histogram for particles with at least 7 consecutive layer hits + if (hitIt->second.hasConsecutiveLayers(7)) { + genParticlePt7LayersHist.Fill(pt); + } + } + } + } + + std::cout << "Generated particles with 11 hits: " << genParticlePtHist.GetEntries() << std::endl; + std::cout << "Generated particles with 7+ consecutive hits: " << genParticlePt7LayersHist.GetEntries() << std::endl; + + // Second pass: analyze reconstructed tracks + std::cout << "Analyzing reconstructed tracks..." << std::endl; + int nROFs = recTree->GetEntries(); + int totalTracks = 0; + int goodTracksCount = 0; + int fakeTracksCount = 0; + + for (int iROF = 0; iROF < nROFs; ++iROF) { + recTree->GetEntry(iROF); + + if (!recTracks || !trkLabels) { + continue; + } + + totalTracks += recTracks->size(); + + for (size_t iTrack = 0; iTrack < recTracks->size(); ++iTrack) { + const auto& track = recTracks->at(iTrack); + const auto& label = trkLabels->at(iTrack); + + if (!label.isSet() || !label.isValid()) { + continue; + } + + int eventID = label.getEventID(); + int trackID = label.getTrackID(); + int nClusters = track.getNumberOfClusters(); + + // Get MC track info + if (eventID < 0 || eventID >= nEvents) { + continue; + } + + const auto& mcTracks = kineReader.getTracks(eventID); + if (trackID < 0 || trackID >= (int)mcTracks.size()) { + continue; + } + + float pt = mcTracks[trackID].GetPt(); + + // Fill histograms + numberOfClustersPerTrack.Fill(nClusters); + + auto key = o2::MCCompLabel(trackID, eventID, 0); + if (particleHitMap.find(key) != particleHitMap.end() && particleHitMap[key].hasConsecutiveLayers(11)) { + if (label.isFake()) { + fakeTracks.Fill(pt); + fakeTracksCount++; + if (nClusters >= 7 && nClusters <= 11) { + fakeTracksMatching[nClusters - 7].Fill(pt); + } + } else { + goodTracks.Fill(pt); + goodTracksCount++; + if (nClusters >= 7 && nClusters <= 11) { + goodTracksMatching[nClusters - 7].Fill(pt); + } + } + } + } + } + + // Create efficiency histograms + std::cout << "Computing efficiencies..." << std::endl; + + std::array efficiencyHistograms; + THStack* efficiencyStack = new THStack("efficiencyStack", + "Tracking Efficiency; #it{p}_{T} (GeV/#it{c}); Efficiency"); + + int colors[5] = {kRed, kBlue, kGreen + 2, kMagenta, kOrange}; + for (int i = 0; i < 5; ++i) { + int nClusters = i + 7; + efficiencyHistograms[i] = TH1D(Form("efficiency_%dClusters", nClusters), + Form("Efficiency for %d cluster tracks; #it{p}_{T} (GeV/#it{c}); Efficiency", nClusters), + nb, xbins); + + efficiencyHistograms[i].Divide(&goodTracksMatching[i], &genParticlePtHist, 1, 1, "B"); + + efficiencyHistograms[i].SetLineColor(colors[i]); + efficiencyHistograms[i].SetFillColor(colors[i]); + efficiencyHistograms[i].SetLineWidth(2); + efficiencyHistograms[i].SetMarkerColor(colors[i]); + efficiencyHistograms[i].SetMarkerStyle(20 + i); + efficiencyStack->Add(&efficiencyHistograms[i]); + } + + // Write output + std::cout << "Writing output to " << outputfile << std::endl; + TFile outFile(outputfile.c_str(), "RECREATE"); + genParticlePtHist.Write(); + goodTracks.Write(); + fakeTracks.Write(); + for (int i = 0; i < 5; ++i) { + goodTracksMatching[i].Write(); + fakeTracksMatching[i].Write(); + efficiencyHistograms[i].Write(); + } + efficiencyStack->Write(); + genParticlePt7LayersHist.Write(); + numberOfClustersPerTrack.Write(); + outFile.Close(); + + // Clean up + hitsFile->Close(); + tracFile->Close(); + delete efficiencyStack; + delete hitsFile; + delete tracFile; +} diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh new file mode 100644 index 0000000000000..797d1d12af4ab --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh @@ -0,0 +1,10 @@ +#Number of events to simulate +nEvents=10 + +# Simulating +o2-sim-serial-run5 -n $nEvents -g pythia8hi -m TRK --configKeyValues "TRKBase.layoutML=kTurboStaves;TRKBase.layoutOL=kStaggered;">& sim_TRK.log + +# Digitizing +o2-sim-digitizer-workflow -b >& digiTRK.log + +root.exe -b -q CheckDigits.C+ >& CheckDigits.log diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..01ddc783d192b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -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. + +o2_add_library(TRKReconstruction + TARGETVARNAME targetName + SOURCES src/TimeFrame.cxx + PUBLIC_LINK_LIBRARIES + O2::ITStracking + O2::GPUCommon + Microsoft.GSL::GSL + O2::CommonConstants + O2::DataFormatsITSMFT + O2::SimulationDataFormat + O2::ITSBase + O2::ITSReconstruction + O2::ITSMFTReconstruction + O2::DataFormatsITS + O2::TRKSimulation + nlohmann_json::nlohmann_json + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb) + +o2_target_root_dictionary(TRKReconstruction + HEADERS include/TRKReconstruction/TimeFrame.h + LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h new file mode 100644 index 0000000000000..f42a1c897efb6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.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 TimeFrame.h +/// \brief TRK TimeFrame class derived from ITS TimeFrame +/// + +#ifndef ALICEO2_TRK_TIMEFRAME_H +#define ALICEO2_TRK_TIMEFRAME_H + +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Constants.h" +#include "ITStracking/Configuration.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include +#include +#include +#include + +#include + +class TTree; + +namespace o2 +{ +namespace trk +{ +class Hit; +class GeometryTGeo; + +/// TRK TimeFrame class that extends ITS TimeFrame functionality +/// This allows for customization of tracking algorithms specific to the TRK detector +template +class TimeFrame : public o2::its::TimeFrame +{ + public: + TimeFrame() = default; + ~TimeFrame() override = default; + + /// Override methods if needed for TRK-specific behavior + /// For now, we inherit all functionality from ITS TimeFrame + + /// Process hits from TTree to initialize ROFs + /// \param hitsTree Tree containing TRK hits + /// \param mcHeaderTree Tree containing MC event headers + /// \param nEvents Number of events to process + /// \param gman TRK geometry manager instance + /// \param config Configuration parameters for hit reconstruction + int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); + + /// Add primary vertices from MC headers for each ROF + /// \param mcHeaderTree Tree containing MC event headers + /// \param nRofs Number of ROFs (Read-Out Frames) + /// \param nEvents Number of events to process + /// \param inROFpileup Number of events per ROF + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_TRK_TIMEFRAME_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h new file mode 100644 index 0000000000000..09ab598ec626c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::trk::TimeFrame < 11> + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx new file mode 100644 index 0000000000000..610a08450d5ee --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -0,0 +1,194 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file TimeFrame.cxx +/// \brief TRK TimeFrame implementation +/// + +#include "TRKReconstruction/TimeFrame.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "Framework/Logger.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include +#include +#include +#include + +namespace o2::trk +{ + +template +int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +{ + constexpr std::array startLayer{0, 3}; + const Long64_t nEvents = hitsTree->GetEntries(); + + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; + + // Calculate number of ROFs and initialize data structures + this->mNrof = (nEvents + inROFpileup - 1) / inROFpileup; + + // Reset and prepare ROF data structures + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mMinR[iLayer] = std::numeric_limits::max(); + this->mMaxR[iLayer] = std::numeric_limits::lowest(); + this->mROFramesClusters[iLayer].clear(); + this->mROFramesClusters[iLayer].resize(this->mNrof + 1, 0); + this->mUnsortedClusters[iLayer].clear(); + this->mTrackingFrameInfo[iLayer].clear(); + this->mClusterExternalIndices[iLayer].clear(); + } + + // Pre-count hits to reserve memory efficiently + int totalNHits{0}; + std::array clusterCountPerLayer{}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + for (const auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; // skip non-barrel hits + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + if (layer >= nLayers) { + continue; + } + ++clusterCountPerLayer[layer]; + totalNHits++; + } + } + + // Reserve memory for all layers + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } + clearResizeBoundedVector(this->mClusterSize, totalNHits, this->mMemoryPool.get()); + + std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; + if (config["geometry"]["pitch"].size() == nLayers) { + for (int iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { + LOGP(info, "Setting resolution for layer {} from config", iLayer); + LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); + resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); + } + } + LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); + + int hitCounter{0}; + auto labels = new dataformats::MCTruthContainer(); + + int iRof{0}; // Current ROF index + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + + for (auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; // skip non-barrel hits for this test + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + float alpha{0.f}; + o2::math_utils::Point3D gloXYZ; + o2::math_utils::Point3D trkXYZ; + float r{0.f}; + if (layer >= nLayers) { + continue; + } + if (layer >= 3) { + int chipID = hit.GetDetectorID(); + alpha = gman->getSensorRefAlphaMLOT(chipID); + const o2::math_utils::Transform3D& l2g = gman->getMatrixL2G(chipID); + auto locXYZ = l2g ^ (hit.GetPos()); + locXYZ.SetX(locXYZ.X() + gRandom->Gaus(0.0, resolution[layer])); + locXYZ.SetZ(locXYZ.Z() + gRandom->Gaus(0.0, resolution[layer])); + gloXYZ = gman->getMatrixL2G(chipID) * locXYZ; + trkXYZ = gman->getMatrixT2L(chipID - gman->getNumberOfActivePartsVD()) ^ locXYZ; + r = std::hypot(gloXYZ.X(), gloXYZ.Y()); + } else { + const auto& hitPos = hit.GetPos(); + r = std::hypot(hitPos.X(), hitPos.Y()); + alpha = std::atan2(hitPos.Y(), hitPos.X()) + gRandom->Gaus(0.0, resolution[layer] / r); + o2::math_utils::bringTo02Pi(alpha); + gloXYZ.SetX(r * std::cos(alpha)); + gloXYZ.SetY(r * std::sin(alpha)); + gloXYZ.SetZ(hitPos.Z() + gRandom->Gaus(0.0, resolution[layer])); + trkXYZ.SetX(r); + trkXYZ.SetY(0.f); + trkXYZ.SetZ(gloXYZ.Z()); + } + this->mMinR[layer] = std::min(this->mMinR[layer], r); + this->mMaxR[layer] = std::max(this->mMaxR[layer], r); + this->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); + /// Rotate to the global frame + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[layer].size()); + this->addClusterExternalIndexToLayer(layer, hitCounter); + MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; + labels->addElement(hitCounter, label); + this->mClusterSize[hitCounter] = 1; // For compatibility with cluster-based tracking, set cluster size to 1 for hits + hitCounter++; + } + trkHit->clear(); + + // Update ROF structure when we complete an ROF or reach the last event + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { + this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum + } + // Update primary vertices ROF structure + } + this->mClusterLabels = labels; + } + return this->mNrof; +} + +template +void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +{ + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + this->mROFramesPV.clear(); + this->mROFramesPV.resize(nRofs + 1, 0); + this->mPrimaryVertices.clear(); + + int iRof{0}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + mcHeaderTree->GetEntry(iEvent); + o2::its::Vertex vertex; + vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + vertex.setNContributors(30); + vertex.setChi2(0.f); + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {})", iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + this->mPrimaryVertices.push_back(vertex); + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + this->mROFramesPV[iRof] = this->mPrimaryVertices.size(); // effectively calculating an exclusive sum + } + } + this->mMultiplicityCutMask.resize(nRofs, true); /// all ROFs are valid with MC primary vertices. +} + +// Explicit template instantiation for TRK with 11 layers +template class TimeFrame<11>; + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index a1cb0279efef8..10f117750d793 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -10,25 +10,33 @@ # or submit itself to any jurisdiction. o2_add_library(TRKSimulation - SOURCES src/TRKLayer.cxx + SOURCES src/Hit.cxx + src/TRKLayer.cxx + src/ChipDigitsContainer.cxx + src/ChipSimResponse.cxx src/Detector.cxx + src/DigiParams.cxx src/Digitizer.cxx src/TRKServices.cxx src/DPLDigitizerParam.cxx - src/TRKPetalCase.cxx - src/TRKPetalLayer.cxx - src/TRKPetalDisk.cxx + src/VDLayer.cxx + src/VDGeometryBuilder.cxx PUBLIC_LINK_LIBRARIES O2::TRKBase O2::FT3Simulation O2::ITSMFTSimulation + O2::DetectorsRaw O2::SimulationDataFormat) o2_target_root_dictionary(TRKSimulation - HEADERS include/TRKSimulation/Digitizer.h + HEADERS include/TRKSimulation/Hit.h + include/TRKSimulation/ChipDigitsContainer.h + include/TRKSimulation/ChipSimResponse.h + include/TRKSimulation/DigiParams.h + include/TRKSimulation/Digitizer.h include/TRKSimulation/Detector.h include/TRKSimulation/TRKLayer.h include/TRKSimulation/TRKServices.h - include/TRKSimulation/TRKPetalCase.h - include/TRKSimulation/TRKPetalLayer.h - include/TRKSimulation/TRKPetalDisk.h + include/TRKSimulation/VDLayer.h + include/TRKSimulation/VDGeometryBuilder.h + include/TRKSimulation/VDSensorRegistry.h include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h new file mode 100644 index 0000000000000..73c95b04c45e3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.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 ALICEO2_TRK_CHIPDIGITSCONTAINER_ +#define ALICEO2_TRK_CHIPDIGITSCONTAINER_ + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSMFTSimulation/ChipDigitsContainer.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKBase/Specs.h" +#include "TRKSimulation/DigiParams.h" +#include + +namespace o2::trk +{ + +class ChipDigitsContainer : public o2::itsmft::ChipDigitsContainer +{ + public: + explicit ChipDigitsContainer(UShort_t idx = 0); + + using Segmentation = SegmentationChip; + + /// Get global ordering key made of readout frame, column and row + static ULong64_t getOrderingKey(UInt_t roframe, UShort_t row, UShort_t col) + { + return (static_cast(roframe) << (8 * sizeof(UInt_t))) + (static_cast(col) << (8 * sizeof(Short_t))) + row; + } + + ClassDefNV(ChipDigitsContainer, 1); +}; + +} // namespace o2::trk + +#endif // ALICEO2_TRK_CHIPDIGITSCONTAINER_ diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipSimResponse.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipSimResponse.h new file mode 100644 index 0000000000000..29147997f66bf --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipSimResponse.h @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_TRKSIMULATION_CHIPSIMRESPONSE_H +#define ALICEO2_TRKSIMULATION_CHIPSIMRESPONSE_H + +#include "ITSMFTSimulation/AlpideSimResponse.h" + +namespace o2 +{ +namespace trk +{ + +class ChipSimResponse : public o2::itsmft::AlpideSimResponse +{ + public: + ChipSimResponse() = default; + ChipSimResponse(const ChipSimResponse& other) = default; + ChipSimResponse(const o2::itsmft::AlpideSimResponse* base) : o2::itsmft::AlpideSimResponse(*base) {} + + void initData(int tableNumber, std::string dataPath, const bool quiet = true); + + ClassDef(ChipSimResponse, 1); +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_TRKSIMULATION_CHIPSIMRESPONSE_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index 59b3551ecbd32..bbafcf3f8f979 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -37,9 +37,9 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper @@ -43,9 +42,9 @@ class Detector : public o2::base::DetImpl void ConstructGeometry() override; - o2::itsmft::Hit* addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, - const TVector3& startMom, double startE, double endTime, double eLoss, - unsigned char startStatus, unsigned char endStatus); + o2::trk::Hit* addHit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos, + const TVector3& startMom, double startE, double endTime, double eLoss, + unsigned char startStatus, unsigned char endStatus); // Mandatory overrides void BeginPrimary() override { ; } @@ -58,8 +57,8 @@ class Detector : public o2::base::DetImpl void Register() override; void Reset() override; - // Custom memer functions - std::vector* getHits(int iColl) const + // Custom member functions + std::vector* getHits(int iColl) const { if (!iColl) { return mHits; @@ -68,7 +67,7 @@ class Detector : public o2::base::DetImpl } void configDefault(); - void buildTRKNewVacuumVessel(); + void buildTRKMiddleOuterLayers(); void configFromFile(std::string fileName = "alice3_TRK_layout.txt"); void configToFile(std::string fileName = "alice3_TRK_layout.txt"); @@ -77,28 +76,40 @@ class Detector : public o2::base::DetImpl void createGeometry(); private: + int mNumberOfVolumes; + int mNumberOfVolumesVD; + // Transient data about track passing the sensor struct TrackData { - bool mHitStarted; // hit creation started - unsigned char mTrkStatusStart; // track status flag - TLorentzVector mPositionStart; // position at entrance - TLorentzVector mMomentumStart; // momentum - double mEnergyLoss; // energy loss - } mTrackData; //! transient data - GeometryTGeo* mGeometryTGeo; //! - std::vector* mHits; // ITSMFT ones for the moment + bool mHitStarted; // hit creation started + unsigned char mTrkStatusStart; // track status flag + TLorentzVector mPositionStart; // position at entrance + TLorentzVector mMomentumStart; // momentum + double mEnergyLoss; // energy loss + } mTrackData; //! transient data + GeometryTGeo* mGeometryTGeo; //! + std::vector* mHits; // ITSMFT ones for the moment std::vector mLayers; - TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker - std::vector mPetalCases; // Houses the Iris tracker and its services. Created fully in the beam pipe + TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker std::vector mFirstOrLastLayers; // Names of the first or last layers bool InsideFirstOrLastLayer(std::string layerName); void defineSensitiveVolumes(); + protected: + std::vector mSensorID; //! layer identifiers + std::vector mSensorName; //! layer names + + public: + static constexpr Int_t sNumberVDPetalCases = 4; //! Number of VD petals + int getNumberOfLayers() const { return mLayers.size(); } //! Number of TRK layers + + void Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int mod, int chip, int chipID) const; + template friend class o2::base::DetImpl; - ClassDefOverride(Detector, 1); + ClassDefOverride(Detector, 2); }; } // namespace trk } // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h new file mode 100644 index 0000000000000..0463a68a77c3e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h @@ -0,0 +1,136 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DigiParams.h +/// \brief Simulation parameters for the TRK digitizer. Based on the ITS2 and ITS3 digitizer parameters + +#ifndef ALICEO2_TRK_DIGIPARAMS_H +#define ALICEO2_TRK_DIGIPARAMS_H + +#include +#include "ITSMFTSimulation/AlpideSignalTrapezoid.h" +#include "ITSMFTSimulation/AlpideSimResponse.h" +#include "TRKBase/TRKBaseParam.h" +#include "TRKBase/GeometryTGeo.h" + +//////////////////////////////////////////////////////////// +// // +// Simulation params for the TRK digitizer // +// // +// This is a provisionary implementation, until proper // +// microscopic simulation and its configuration will // +// be implemented // +// // +//////////////////////////////////////////////////////////// + +namespace o2 +{ +namespace trk +{ + +class ChipSimResponse; + +class DigiParams +{ + + using SignalShape = o2::itsmft::AlpideSignalTrapezoid; + + public: + DigiParams(); + ~DigiParams() = default; + + void setNoisePerPixel(float v) { mNoisePerPixel = v; } + float getNoisePerPixel() const { return mNoisePerPixel; } + + void setContinuous(bool v) { mIsContinuous = v; } + bool isContinuous() const { return mIsContinuous; } + + int getROFrameLengthInBC() const { return mROFrameLengthInBC; } + void setROFrameLengthInBC(int n) { mROFrameLengthInBC = n; } + + void setROFrameLength(float ns); + float getROFrameLength() const { return mROFrameLength; } + float getROFrameLengthInv() const { return mROFrameLengthInv; } + + void setStrobeDelay(float ns) { mStrobeDelay = ns; } + float getStrobeDelay() const { return mStrobeDelay; } + + void setStrobeLength(float ns) { mStrobeLength = ns; } + float getStrobeLength() const { return mStrobeLength; } + + void setTimeOffset(double sec) { mTimeOffset = sec; } + double getTimeOffset() const { return mTimeOffset; } + + void setROFrameBiasInBC(int n) { mROFrameBiasInBC = n; } + int getROFrameBiasInBC() const { return mROFrameBiasInBC; } + + void setChargeThreshold(int v, float frac2Account = 0.1); + void setNSimSteps(int v); + void setEnergyToNElectrons(float v) { mEnergyToNElectrons = v; } + + void setVbb(float v) { mVbb = v; } + void setIBVbb(float v) { mIBVbb = v; } + void setOBVbb(float v) { mOBVbb = v; } + + int getChargeThreshold() const { return mChargeThreshold; } + int getMinChargeToAccount() const { return mMinChargeToAccount; } + int getNSimSteps() const { return mNSimSteps; } + float getNSimStepsInv() const { return mNSimStepsInv; } + float getEnergyToNElectrons() const { return mEnergyToNElectrons; } + + float getVbb() const { return mVbb; } + float getIBVbb() const { return mIBVbb; } + float getOBVbb() const { return mOBVbb; } + + bool isTimeOffsetSet() const { return mTimeOffset > -infTime; } + + const o2::trk::ChipSimResponse* getAlpSimResponse() const { return mAlpSimResponse.get(); } + void setAlpSimResponse(const o2::itsmft::AlpideSimResponse*); + + const SignalShape& getSignalShape() const { return mSignalShape; } + SignalShape& getSignalShape() { return (SignalShape&)mSignalShape; } + + virtual void print() const; + + private: + static constexpr double infTime = 1e99; + bool mIsContinuous = false; ///< flag for continuous simulation + float mNoisePerPixel = 1.e-8; ///< ALPIDE Noise per chip + int mROFrameLengthInBC = 0; ///< ROF length in BC for continuos mode + float mROFrameLength = 0; ///< length of RO frame in ns + float mStrobeDelay = 0.; ///< strobe start (in ns) wrt ROF start + float mStrobeLength = 0; ///< length of the strobe in ns (sig. over threshold checked in this window only) + double mTimeOffset = -2 * infTime; ///< time offset (in seconds!) to calculate ROFrame from hit time + int mROFrameBiasInBC = 0; ///< misalignment of the ROF start in BC + int mChargeThreshold = 1; ///< charge threshold in Nelectrons + int mMinChargeToAccount = 1; ///< minimum charge contribution to account + int mNSimSteps = 475; ///< number of steps in response simulation + float mNSimStepsInv = 1. / mNSimSteps; ///< its inverse + + float mEnergyToNElectrons = 1. / 3.6e-9; // conversion of eloss to Nelectrons + + float mVbb = 0.0; ///< back bias absolute value for MFT (in Volt) + float mIBVbb = 0.0; ///< back bias absolute value for ITS Inner Barrel (in Volt) + float mOBVbb = 0.0; ///< back bias absolute value for ITS Outter Barrel (in Volt) + + o2::itsmft::AlpideSignalTrapezoid mSignalShape; ///< signal timeshape parameterization + + std::unique_ptr mAlpSimResponse; //!< pointer on external response + + // auxiliary precalculated parameters + float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns + + // ClassDef(DigiParams, 2); +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h index 6863c5392cae3..362de63fb8cb6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h @@ -21,12 +21,12 @@ #include "Rtypes.h" // for Digitizer::Class #include "TObject.h" // for TObject -#include "ITSMFTSimulation/ChipDigitsContainer.h" -// #include "ITSMFTSimulation/AlpideSimResponse.h" -#include "ITSMFTSimulation/DigiParams.h" -#include "ITSMFTSimulation/Hit.h" +#include "TRKSimulation/ChipSimResponse.h" +#include "TRKSimulation/ChipDigitsContainer.h" + +#include "TRKSimulation/DigiParams.h" +#include "TRKSimulation/Hit.h" #include "TRKBase/GeometryTGeo.h" -// #include "ITS3Base/SegmentationSuperAlpide.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "CommonDataFormat/InteractionRecord.h" @@ -37,7 +37,7 @@ namespace o2::trk { -class Digitizer : public TObject +class Digitizer { using ExtraDig = std::vector; ///< container for extra contributions to PreDigits @@ -45,14 +45,17 @@ class Digitizer : public TObject void setDigits(std::vector* dig) { mDigits = dig; } void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } void setROFRecords(std::vector* rec) { mROFRecords = rec; } + void setResponseName(const std::string& name) { mRespName = name; } - o2::itsmft::DigiParams& getParams() { return (o2::itsmft::DigiParams&)mParams; } - const o2::itsmft::DigiParams& getParams() const { return mParams; } + o2::trk::DigiParams& getParams() { return (o2::trk::DigiParams&)mParams; } + const o2::trk::DigiParams& getParams() const { return mParams; } void init(); + const o2::trk::ChipSimResponse* getChipResponse(int chipID); + /// Steer conversion of hits to digits - void process(const std::vector* hits, int evID, int srcID); + void process(const std::vector* hits, int evID, int srcID); void setEventTime(const o2::InteractionTimeRecord& irt); double getEndTimeOfROFMax() const { @@ -64,10 +67,9 @@ class Digitizer : public TObject bool isContinuous() const { return mParams.isContinuous(); } void fillOutputContainer(uint32_t maxFrame = 0xffffffff); - void setDigiParams(const o2::itsmft::DigiParams& par) { mParams = par; } - const o2::itsmft::DigiParams& getDigitParams() const { return mParams; } + const o2::trk::DigiParams& getDigitParams() const { return mParams; } - // provide the common itsmft::GeometryTGeo to access matrices and segmentation + // provide the common trk::GeometryTGeo to access matrices and segmentation void setGeometry(const o2::trk::GeometryTGeo* gm) { mGeometry = gm; } uint32_t getEventROFrameMin() const { return mEventROFrameMin; } @@ -81,8 +83,8 @@ class Digitizer : public TObject void setDeadChannelsMap(const o2::itsmft::NoiseMap* mp) { mDeadChanMap = mp; } private: - void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID); - void registerDigits(o2::itsmft::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, + void processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, int srcID); + void registerDigits(o2::trk::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl); ExtraDig* getExtraDigBuffer(uint32_t roFrame) @@ -97,9 +99,37 @@ class Digitizer : public TObject return mExtraBuff[ind].get(); } + /// Get the number of columns according to the subdetector + /// \param subDetID 0 for VD, 1 for ML/OT + /// \param layer 0 to 2 for VD, 0 to 7 for ML/OT + /// \return Number of columns (In the entire layer(VD) or chip (ML/OT) + int getNCols(int subDetID, int layer) + { + if (subDetID == 0) { // VD + return constants::VD::petal::layer::nCols; + } else if (subDetID == 1) { // ML/OT: the smallest element is a chip of 470 rows and 640 cols + return constants::moduleMLOT::chip::nCols; + } + return 0; + } + + /// Get the number of rows according to the subdetector + /// \param subDetID 0 for VD, 1 for ML/OT + /// \param layer 0 to 2 for VD, 0 to 7 for ML/OT + /// \return Number of rows (In the entire layer(VD) or chip (ML/OT) + int getNRows(int subDetID, int layer) + { + if (subDetID == 0) { // VD + return constants::VD::petal::layer::nRows[layer]; + } else if (subDetID == 1) { // ML/OT + return constants::moduleMLOT::chip::nRows; + } + return 0; + } + static constexpr float sec2ns = 1e9; - o2::itsmft::DigiParams mParams; ///< digitization parameters + o2::trk::DigiParams mParams; ///< digitization parameters o2::InteractionTimeRecord mEventTime; ///< global event time and interaction record o2::InteractionRecord mIRFirstSampledTF; ///< IR of the 1st sampled IR, noise-only ROFs will be inserted till this IR only double mCollisionTimeWrtROF{}; @@ -107,22 +137,39 @@ class Digitizer : public TObject uint32_t mROFrameMax = 0; ///< highest RO frame of current digits uint32_t mNewROFrame = 0; ///< ROFrame corresponding to provided time + bool mIsBeforeFirstRO = false; + uint32_t mEventROFrameMin = 0xffffffff; ///< lowest RO frame for processed events (w/o automatic noise ROFs) uint32_t mEventROFrameMax = 0; ///< highest RO frame forfor processed events (w/o automatic noise ROFs) - o2::itsmft::AlpideSimResponse* mAlpSimResp = nullptr; // simulated response + int mNumberOfChips = 0; + + const o2::trk::ChipSimResponse* mChipSimResp = nullptr; // simulated response + const o2::trk::ChipSimResponse* mChipSimRespVD = nullptr; // simulated response for VD chips + const o2::trk::ChipSimResponse* mChipSimRespMLOT = nullptr; // simulated response for ML/OT chips + + std::string mRespName; /// APTS or ALICE3, depending on the response to be used + + bool mSimRespOrientation{false}; // wether the orientation in the response function is flipped + float mSimRespVDShift{0.f}; // adjusting the Y-shift in the APTS response function to match sensor local coord. + float mSimRespVDScaleX{1.f}; // scale x-local coordinate to response function x-coordinate + float mSimRespVDScaleZ{1.f}; // scale z-local coordinate to response function z-coordinate + float mSimRespMLOTShift{0.f}; // adjusting the Y-shift in the APTS response function to match sensor local coord. + float mSimRespMLOTScaleX{1.f}; // scale x-local coordinate to response function x-coordinate + float mSimRespMLOTScaleZ{1.f}; // scale z-local coordinate to response function z-coordinate + float mSimRespVDScaleDepth{1.f}; // scale depth-local coordinate to response function depth-coordinate + float mSimRespMLOTScaleDepth{1.f}; // scale depth-local coordinate to response function depth-coordinate const o2::trk::GeometryTGeo* mGeometry = nullptr; ///< TRK geometry - std::vector mChips; ///< Array of chips digits containers - std::deque> mExtraBuff; ///< burrer (per roFrame) for extra digits + std::vector mChips; ///< Array of chips digits containers + std::deque> mExtraBuff; ///< buffer (per roFrame) for extra digits std::vector* mDigits = nullptr; //! output digits std::vector* mROFRecords = nullptr; //! output ROF records o2::dataformats::MCTruthContainer* mMCLabels = nullptr; //! output labels const o2::itsmft::NoiseMap* mDeadChanMap = nullptr; - - ClassDef(Digitizer, 1); + const o2::itsmft::NoiseMap* mNoiseMap = nullptr; }; -} // namespace o2::trk \ No newline at end of file +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h new file mode 100644 index 0000000000000..a178c30069f14 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h @@ -0,0 +1,149 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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 TRK Hit class + +#ifndef ALICEO2_TRK_POINT_H_ +#define ALICEO2_TRK_POINT_H_ + +#include "SimulationDataFormat/BaseHits.h" // for BasicXYZEHit +#include "Rtypes.h" // for Bool_t, Double_t, Int_t, Double32_t, etc +#include "TVector3.h" // for TVector3 +#include +#include "CommonUtils/ShmAllocator.h" + +namespace o2 +{ +namespace trk +{ + +class Hit : public o2::BasicXYZEHit +{ + + public: + enum HitStatus_t { + kTrackEntering = 0x1, + kTrackInside = 0x1 << 1, + kTrackExiting = 0x1 << 2, + kTrackOut = 0x1 << 3, + kTrackStopped = 0x1 << 4, + kTrackAlive = 0x1 << 5 + }; + + /// Default constructor + Hit() = default; + + /// Class Constructor + /// \param trackID Index of MCTrack + /// \param detID Detector ID + /// \param startPos Coordinates at entrance to active volume [cm] + /// \param pos Coordinates to active volume [cm] + /// \param mom Momentum of track at entrance [GeV] + /// \param endTime Time at entrance [ns] + /// \param time Time since event start [ns] + /// \param eLoss Energy deposit [GeV] + /// \param startStatus: status at entrance + /// \param endStatus: status at exit + inline Hit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& pos, const TVector3& mom, double startE, + double endTime, double eLoss, unsigned char statusStart, unsigned char status); + + // Entrance position getters + math_utils::Point3D GetPosStart() const { return mPosStart; } + Float_t GetStartX() const { return mPosStart.X(); } + Float_t GetStartY() const { return mPosStart.Y(); } + Float_t GetStartZ() const { return mPosStart.Z(); } + template + void GetStartPosition(F& x, F& y, F& z) const + { + x = GetStartX(); + y = GetStartY(); + z = GetStartZ(); + } + // momentum getters + math_utils::Vector3D GetMomentum() const { return mMomentum; } + math_utils::Vector3D& GetMomentum() { return mMomentum; } + Float_t GetPx() const { return mMomentum.X(); } + Float_t GetPy() const { return mMomentum.Y(); } + Float_t GetPz() const { return mMomentum.Z(); } + Float_t GetE() const { return mE; } + Float_t GetTotalEnergy() const { return GetE(); } + + UChar_t GetStatusEnd() const { return mTrackStatusEnd; } + UChar_t GetStatusStart() const { return mTrackStatusStart; } + + Bool_t IsEntering() const { return mTrackStatusEnd & kTrackEntering; } + Bool_t IsInside() const { return mTrackStatusEnd & kTrackInside; } + Bool_t IsExiting() const { return mTrackStatusEnd & kTrackExiting; } + Bool_t IsOut() const { return mTrackStatusEnd & kTrackOut; } + Bool_t IsStopped() const { return mTrackStatusEnd & kTrackStopped; } + Bool_t IsAlive() const { return mTrackStatusEnd & kTrackAlive; } + + Bool_t IsEnteringStart() const { return mTrackStatusStart & kTrackEntering; } + Bool_t IsInsideStart() const { return mTrackStatusStart & kTrackInside; } + Bool_t IsExitingStart() const { return mTrackStatusStart & kTrackExiting; } + Bool_t IsOutStart() const { return mTrackStatusStart & kTrackOut; } + Bool_t IsStoppedStart() const { return mTrackStatusStart & kTrackStopped; } + Bool_t IsAliveStart() const { return mTrackStatusStart & kTrackAlive; } + + // Entrance position setter + void SetPosStart(const math_utils::Point3D& p) { mPosStart = p; } + + /// Output to screen + void Print(const Option_t* opt) const; + friend std::ostream& operator<<(std::ostream& of, const Hit& point) + { + of << "-I- Hit: O2its point for track " << point.GetTrackID() << " in detector " << point.GetDetectorID() << std::endl; + /* + of << " Position (" << point.fX << ", " << point.fY << ", " << point.fZ << ") cm" << std::endl; + of << " Momentum (" << point.fPx << ", " << point.fPy << ", " << point.fPz << ") GeV" << std::endl; + of << " Time " << point.fTime << " ns, Length " << point.fLength << " cm, Energy loss " + << point.fELoss * 1.0e06 << " keV" << std::endl; + */ + return of; + } + + private: + math_utils::Vector3D mMomentum; ///< momentum at entrance + math_utils::Point3D mPosStart; ///< position at entrance (base mPos give position on exit) + Float_t mE; ///< total energy at entrance + UChar_t mTrackStatusEnd; ///< MC status flag at exit + UChar_t mTrackStatusStart; ///< MC status at starting point + + ClassDefNV(Hit, 1); +}; + +Hit::Hit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos, const TVector3& startMom, + double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus) + : BasicXYZEHit(endPos.X(), endPos.Y(), endPos.Z(), endTime, eLoss, trackID, detID), + mMomentum(startMom.Px(), startMom.Py(), startMom.Pz()), + mPosStart(startPos.X(), startPos.Y(), startPos.Z()), + mE(startE), + mTrackStatusEnd(endStatus), + mTrackStatusStart(startStatus) +{ +} + +} // namespace trk +} // namespace o2 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; +} // namespace std + +#endif + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index ba894f6d7a92b..0a7a45e87bfd8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -16,6 +16,7 @@ #include #include "TRKBase/TRKBaseParam.h" +#include "TRKBase/Specs.h" namespace o2 { @@ -25,23 +26,27 @@ class TRKLayer { public: TRKLayer() = default; - TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, float zLength, float layerX2X0); - TRKLayer(int layerNumber, std::string layerName, float rInn, float zLength, float thick); + TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, int numberOfModules, float layerX2X0); + TRKLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thick); ~TRKLayer() = default; void setLayout(eLayout layout) { mLayout = layout; }; auto getInnerRadius() const { return mInnerRadius; } auto getOuterRadius() const { return mOuterRadius; } - auto getZ() const { return mZ; } + auto getZ() const { return constants::moduleMLOT::length * mNumberOfModules; } auto getx2X0() const { return mX2X0; } auto getChipThickness() const { return mChipThickness; } auto getNumber() const { return mLayerNumber; } auto getName() const { return mLayerName; } - TGeoVolume* createSensor(std::string type, double width = -1); - TGeoVolume* createChip(std::string type, double width = -1); - TGeoVolume* createStave(std::string type, double width = -1); + TGeoVolume* createSensor(std::string type); + TGeoVolume* createDeadzone(std::string type); + TGeoVolume* createMetalStack(std::string type); + TGeoVolume* createChip(std::string type); + TGeoVolume* createModule(std::string type); + TGeoVolume* createStave(std::string type); + TGeoVolume* createHalfStave(std::string type); void createLayer(TGeoVolume* motherVolume); private: @@ -49,16 +54,20 @@ class TRKLayer static constexpr float mLogicalVolumeThickness = 1; int mLayerNumber; + eLayout mLayout; std::string mLayerName; float mInnerRadius; float mOuterRadius; - float mZ; + int mNumberOfModules; float mX2X0; + float mChipWidth; + float mChipLength; float mChipThickness; - float mModuleWidth; // u.m. = cm - eLayout mLayout; + float mDeadzoneWidth; + float mSensorThickness; + int mHalfNumberOfChips; - ClassDef(TRKLayer, 1); + ClassDef(TRKLayer, 2); }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h deleted file mode 100644 index cd45cc98fd177..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_TRK_PETALCASE_H -#define ALICEO2_TRK_PETALCASE_H - -#include - -#include "TRKSimulation/TRKPetalLayer.h" -#include "TRKSimulation/TRKPetalDisk.h" -#include "TGeoCompositeShape.h" - -namespace o2 -{ -namespace trk -{ -class TRKPetalCase -{ - public: - TRKPetalCase() = default; - TRKPetalCase(Int_t number, TGeoVolume* motherVolume, Bool_t irisOpen); - ~TRKPetalCase() = default; - - // Sensitive volume list - std::vector mPetalLayers; - std::vector mPetalDisks; - - auto getPetalCaseName() { return mPetalCaseName; } - TString getFullName(); - - private: - void constructCase(TGeoVolume* motherVolume); - void constructColdPlate(TGeoVolume* motherVolume); - void constructDetectionPetals(TGeoVolume* motherVolume); - void addDetectionPetelsToFullComposite(); - - void addToPetalCaseComposite(TString shape) { mFullCompositeFormula += ("+" + shape); } - - Int_t mPetalCaseNumber; // Used to determine rotation and position. 0-3 - Bool_t mOpenState; // At injection energy, the iris tracker is in the open position. During stable beams, it is closed - - TString mPetalCaseName; - TString mFullCompositeFormula; // Used to excavate the petal and all its components from the vacuum - - // Center position of the petal case. 0,0,0 at stable beams (a.k.a. closed state) - Double_t mXPos, mYPos, mZPos; - - Double_t mWallThickness; // cm // Assume all the walls have the same thickness for now - Double_t mRIn; // cm - Double_t mROut; // cm - Double_t mRInOpenState; // cm - Double_t mPetalCaseLength; // cm - - Double_t mAngularCoverageAzimuthalWall; // Rad // Angular coverage of azimuthal part of wall (equivalent to that of the sensitive volumes) - Double_t mAngularCoverageRadialWall; // Rad // Angular coverage of radial part of wall - Double_t mToDeg; - - // Petal case parts -> In one composite shape - TGeoTubeSeg* mInnerAzimuthalWall; - TGeoTubeSeg* mOuterAzimuthalWall; - TGeoTubeSeg* mRadialWall; - TGeoTubeSeg* mForwardWall; - - TGeoRotation* mAzimuthalWallRot; - TGeoRotation* mRadialWall1Rot; - TGeoRotation* mRadialWall2Rot; - - TGeoCombiTrans* mAzimuthalWallCombiTrans; - TGeoCombiTrans* mRadialWall1CombiTrans; - TGeoCombiTrans* mRadialWall2CombiTrans; - TGeoCombiTrans* mForwardWall1CombiTrans; - TGeoCombiTrans* mForwardWall2CombiTrans; - - TGeoVolume* mPetalCaseVolume; - - // Cold plate - TGeoTubeSeg* mColdPlate; - TGeoVolume* mColdPlateVolume; - - ClassDef(TRKPetalCase, 1); -}; - -} // namespace trk -} // namespace o2 -#endif // ALICEO2_TRK_PETALCASE_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h deleted file mode 100644 index 465f52eb8d41b..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TRKPetalDisk.h -/// \brief Definition of the TRKPetalDisk class - -#ifndef ALICEO2_TRK_PETAL_DISK_H_ -#define ALICEO2_TRK_PETAL_DISK_H_ - -#include "TGeoManager.h" // for gGeoManager -#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc -#include // for LOG - -namespace o2 -{ -namespace trk -{ - -/// This class defines the Geometry for the TRK Disk TGeo. -class TRKPetalDisk -{ - public: - TRKPetalDisk() = default; - TRKPetalDisk(Int_t diskNumber, std::string diskName, Float_t z, Float_t rIn, Float_t rOut, Float_t angularCoverage, Float_t Diskx2X0); - ~TRKPetalDisk() = default; - - auto getInnerRadius() const { return mInnerRadius; } - auto getOuterRadius() const { return mOuterRadius; } - auto getThickness() const { return mChipThickness; } - auto getAngularCoverage() const { return mAngularCoverage; } - auto getZ() const { return mZ; } - auto getx2X0() const { return mx2X0; } - auto getName() const { return mDiskName; } - auto getSensorName() const { return mSensorName; } - - /// Creates the actual Disk and places inside its mother volume - /// \param motherVolume the TGeoVolume owing the volume structure - void createDisk(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans); - - private: - Int_t mDiskNumber = -1; ///< Current disk number - std::string mDiskName; ///< Current disk name - std::string mSensorName; - Double_t mInnerRadius; ///< Inner radius of this disk - Double_t mOuterRadius; ///< Outer radius of this disk - Double_t mAngularCoverage; - Double_t mZ; ///< Z position of the disk - Double_t mChipThickness; ///< Chip thickness - Double_t mx2X0; ///< Disk material budget x/X0 - - ClassDef(TRKPetalDisk, 1); -}; -} // namespace trk -} // namespace o2 - -#endif // ALICEO2_TRK_PETAL_DISK_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h deleted file mode 100644 index 4e7a7735d51f0..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.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. - -#ifndef ALICEO2_TRK_PETAL_LAYER_H -#define ALICEO2_TRK_PETAL_LAYER_H - -#include "TGeoManager.h" -#include -#include "TGeoTube.h" - -#include "TRKBase/TRKBaseParam.h" - -namespace o2 -{ -namespace trk -{ -class TRKPetalLayer -{ - public: - TRKPetalLayer() = default; - TRKPetalLayer(Int_t layerNumber, std::string layerName, Float_t rIn, Float_t angularCoverage, Float_t zLength, Float_t layerX2X0); - ~TRKPetalLayer() = default; - - auto getInnerRadius() const { return mInnerRadius; } - auto getAngularCoverage() const { return mAngularCoverage; } - auto getZLength() { return mZ; } - auto getx2X0() const { return mX2X0; } - auto getChipThickness() const { return mChipThickness; } - auto getNumber() const { return mLayerNumber; } - auto getName() const { return mLayerName; } - auto getSensorName() const { return mSensorName; } - - void createLayer(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans); - - private: - Int_t mLayerNumber; - std::string mLayerName; - std::string mSensorName; - Float_t mInnerRadius; - Float_t mZ; - Float_t mX2X0; - Float_t mChipThickness; - Float_t mModuleWidth; // u.m. = cm - Float_t mAngularCoverage; // rad - - TGeoTubeSeg* mLayer; - - ClassDef(TRKPetalLayer, 1); -}; - -} // namespace trk -} // namespace o2 -#endif // ALICEO2_TRK_PETAL_LAYER_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h new file mode 100644 index 0000000000000..c337ddb102147 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_VDGEOMETRYBUILDER_H +#define O2_TRK_VDGEOMETRYBUILDER_H + +class TGeoVolume; + +#include +#include + +namespace o2::trk +{ + +// Build full VD for each design. +// Each function builds one local petal assembly (walls + layers + disks) +// and then places/rotates the petal once into the mother volume. + +void createIRISGeometryFullCyl(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry (no petals, no gaps, no side walls) +void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry (no petals, no gaps, no side walls) incl. disks +void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 +void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 +void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 + +void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID = 0, int nPetals = 4, bool rectangularL0 = false); + +} // namespace o2::trk + +#endif // O2_TRK_VDGEOMETRYBUILDER_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h new file mode 100644 index 0000000000000..acf9b19342e4b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h @@ -0,0 +1,117 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_VD_LAYER_H +#define ALICEO2_VD_LAYER_H + +#include +#include + +class TGeoVolume; +class TGeoMatrix; + +namespace o2 +{ +namespace trk +{ + +// Base class for a VD layer +class VDLayer +{ + public: + VDLayer() = default; + VDLayer(int layerNumber, const std::string& layerName, double layerX2X0); + virtual ~VDLayer() = default; + + // Create the layer (AIR container + sensors) and insert it into mother + virtual void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const = 0; + + double getChipThickness() const { return mChipThickness; } + + protected: + int mLayerNumber{0}; + std::string mLayerName; + double mX2X0{0.f}; // Radiation length in units of X0 + double mChipThickness{0.f}; // thickness derived from X/X0 + double mSensorThickness{0.f}; // + double mModuleWidth{4.54f}; // cm + + // ClassDef(VDLayer, 1) +}; + +// Cylindrical segment layer +class VDCylindricalLayer : public VDLayer +{ + public: + VDCylindricalLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double radius, double phiSpanDeg, double lengthZ, double lengthSensZ); + + TGeoVolume* createSensor() const; // builds the sensor volume + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + private: + double mRadius{0.f}; + double mPhiSpanDeg{0.f}; // degrees + double mLengthZ{0.f}; // layer container length in Z + double mLengthSensZ{0.f}; // sensor length in Z + + // ClassDef(VDCylindricalLayer, 1) +}; + +// Rectangular segment layer +class VDRectangularLayer : public VDLayer +{ + public: + VDRectangularLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double width, double lengthZ, double lengthSensZ); + + TGeoVolume* createSensor() const; + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + private: + double mWidth{0.f}; + double mLengthZ{0.f}; + double mLengthSensZ{0.f}; + + // ClassDef(VDRectangularLayer, 1) +}; + +// Disk segment layer +class VDDiskLayer : public VDLayer +{ + public: + VDDiskLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double rMin, double rMax, double phiSpanDeg, double zPos); + + TGeoVolume* createSensor() const; + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + double getZPosition() const { return mZPos; } + + private: + double mRMin{0.f}; + double mRMax{0.f}; + double mPhiSpanDeg{0.f}; // degrees + double mZPos{0.f}; // placement along Z + + // ClassDef(VDDiskLayer, 1) +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_VD_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h new file mode 100644 index 0000000000000..c4fa222e1f4ef --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.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_TRK_VDSENSORREGISTRY_H +#define O2_TRK_VDSENSORREGISTRY_H + +#include +#include + +namespace o2::trk +{ + +struct VDSensorDesc { + enum class Region { Barrel, + Disk }; + enum class Type { Curved, + Plane, + }; + std::string name; // sensor volume name + int petal = -1; + Region region = Region::Barrel; + Type type = Type::Curved; + int idx = -1; // layer or disk index +}; + +// Accessor (defined in VDGeometryBuilder.cxx) +std::vector& vdSensorRegistry(); + +// Utilities (defined in VDGeometryBuilder.cxx) +void clearVDSensorRegistry(); +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx); + +} // namespace o2::trk +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/ChipDigitsContainer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/ChipDigitsContainer.cxx new file mode 100644 index 0000000000000..9ed4a4bedf5c5 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/ChipDigitsContainer.cxx @@ -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. + +#include "TRKSimulation/ChipDigitsContainer.h" + +using namespace o2::trk; + +ChipDigitsContainer::ChipDigitsContainer(UShort_t idx) + : o2::itsmft::ChipDigitsContainer(idx) {} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/ChipSimResponse.cxx similarity index 68% rename from Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx rename to Detectors/Upgrades/ALICE3/TRK/simulation/src/ChipSimResponse.cxx index 81087744b04a9..70c4f131b9724 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/ChipSimResponse.cxx @@ -9,14 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSReconstruction/CookedConfigParam.h" +#include "TRKSimulation/ChipSimResponse.h" +#include +#include -namespace o2 -{ -namespace its -{ -static auto& sITSCookedTrackerParam = o2::its::CookedConfigParam::Instance(); +using namespace o2::trk; -O2ParamImpl(o2::its::CookedConfigParam); -} // namespace its -} // namespace o2 +void ChipSimResponse::initData(int tableNumber, std::string dataPath, const bool quiet) +{ + AlpideSimResponse::initData(tableNumber, dataPath, quiet); +} \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 9b8ffc07b2d0e..556b016f22553 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -16,16 +16,22 @@ #include #include "DetectorsBase/Stack.h" -#include "ITSMFTSimulation/Hit.h" +#include "TRKSimulation/Hit.h" #include "TRKSimulation/Detector.h" #include "TRKBase/TRKBaseParam.h" +#include "TRKSimulation/VDGeometryBuilder.h" +#include "TRKSimulation/VDSensorRegistry.h" -using o2::itsmft::Hit; +#include +#include + +using o2::trk::Hit; namespace o2 { namespace trk { + float getDetLengthFromEta(const float eta, const float radius) { return 2. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta)))); @@ -34,21 +40,21 @@ float getDetLengthFromEta(const float eta, const float radius) Detector::Detector() : o2::base::DetImpl("TRK", true), mTrackData(), - mHits(o2::utils::createSimVector()) + mHits(o2::utils::createSimVector()) { } Detector::Detector(bool active) : o2::base::DetImpl("TRK", true), mTrackData(), - mHits(o2::utils::createSimVector()) + mHits(o2::utils::createSimVector()) { auto& trkPars = TRKBaseParam::Instance(); if (trkPars.configFile != "") { configFromFile(trkPars.configFile); } else { - buildTRKNewVacuumVessel(); + buildTRKMiddleOuterLayers(); configToFile(); configServices(); } @@ -80,43 +86,58 @@ void Detector::configDefault() mLayers.clear(); LOGP(warning, "Loading Scoping Document configuration for ALICE3 TRK"); - // mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 0.5f, 50.f, 100.e-4); - // mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 1.2f, 50.f, 100.e-4); - // mLayers.emplace_back(2, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(2)}, 2.5f, 50.f, 100.e-4); - mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 3.78f, 124.f, 100.e-3); - mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 7.f, 124.f, 100.e-3); - mLayers.emplace_back(2, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(2)}, 12.f, 124.f, 100.e-3); - mLayers.emplace_back(3, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(3)}, 20.f, 124.f, 100.e-3); - mLayers.emplace_back(4, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(4)}, 30.f, 124.f, 100.e-3); - mLayers.emplace_back(5, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(5)}, 45.f, 258.f, 100.e-3); - mLayers.emplace_back(6, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(6)}, 60.f, 258.f, 100.e-3); - mLayers.emplace_back(7, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(7)}, 80.f, 258.f, 100.e-3); + mLayers.emplace_back(0, GeometryTGeo::getTRKLayerPattern() + std::to_string(0), 3.78f, 10, 100.e-3); + mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 7.f, 10, 100.e-3); + mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 12.f, 10, 100.e-3); + mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); + mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); + mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); + mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); + mLayers.emplace_back(7, GeometryTGeo::getTRKLayerPattern() + std::to_string(7), 80.f, 20, 100.e-3); } -void Detector::buildTRKNewVacuumVessel() +void Detector::buildTRKMiddleOuterLayers() { - // Build the TRK detector according to changes proposed during - // https://indico.cern.ch/event/1407704/ - // to adhere to the changes that were presented at the ALICE 3 Upgrade days in March 2024 - // L3 -> 7 cm, L4 -> 9 cm + auto& trkPars = TRKBaseParam::Instance(); mLayers.clear(); - LOGP(warning, "Loading \"After Upgrade Days March 2024\" configuration for ALICE3 TRK"); - // mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 0.5f, 50.f, 100.e-4); - // mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 1.2f, 50.f, 100.e-4); - // mLayers.emplace_back(2, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(2)}, 2.5f, 50.f, 100.e-4); - mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 7.f, 124.f, 100.e-3); - LOGP(info, "TRKLayer created. Name: {}", std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}); - mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 9.f, 124.f, 100.e-3); - mLayers.emplace_back(2, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(2)}, 12.f, 124.f, 100.e-3); - mLayers.emplace_back(3, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(3)}, 20.f, 124.f, 100.e-3); - mLayers.emplace_back(4, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(4)}, 30.f, 124.f, 100.e-3); - mLayers.emplace_back(5, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(5)}, 45.f, 258.f, 100.e-3); - mLayers.emplace_back(6, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(6)}, 60.f, 258.f, 100.e-3); - mLayers.emplace_back(7, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(7)}, 80.f, 258.f, 100.e-3); - - auto& trkPars = TRKBaseParam::Instance(); + switch (trkPars.overallGeom) { + case kDefaultRadii: + // Build the TRK detector according to changes proposed during + // https://indico.cern.ch/event/1407704/ + // to adhere to the changes that were presented at the ALICE 3 Upgrade days in March 2024 + // L3 -> 7 cm, L4 -> 9 cm, L5 -> 12 cm, L6 -> 20 cm + + LOGP(warning, "Loading \"After Upgrade Days March 2024\" configuration for ALICE3 TRK"); + LOGP(warning, "Building TRK with new vacuum vessel and L3 at 7 cm, L4 at 9 cm, L5 at 12 cm, L6 at 20 cm"); + mLayers.emplace_back(0, GeometryTGeo::getTRKLayerPattern() + std::to_string(0), 7.f, 10, 100.e-3); + LOGP(info, "TRKLayer created. Name: {}", GeometryTGeo::getTRKLayerPattern() + std::to_string(0)); + mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 9.f, 10, 100.e-3); + mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 12.f, 10, 100.e-3); + mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); + mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); + mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); + mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); + mLayers.emplace_back(7, GeometryTGeo::getTRKLayerPattern() + std::to_string(7), 80.f, 20, 100.e-3); + break; + case kModRadii: + LOGP(warning, "Loading \"Alternative\" configuration for ALICE3 TRK"); + LOGP(warning, "Building TRK with new vacuum vessel and L3 at 7 cm, L4 at 11 cm, L5 at 15 cm, L6 at 19 cm"); + mLayers.emplace_back(0, GeometryTGeo::getTRKLayerPattern() + std::to_string(0), 7.f, 10, 100.e-3); + LOGP(info, "TRKLayer created. Name: {}", GeometryTGeo::getTRKLayerPattern() + std::to_string(0)); + mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 11.f, 10, 100.e-3); + mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 15.f, 10, 100.e-3); + mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); + mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); + mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); + mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); + mLayers.emplace_back(7, GeometryTGeo::getTRKLayerPattern() + std::to_string(7), 80.f, 20, 100.e-3); + break; + default: + LOGP(fatal, "Unknown option {} for buildTRKMiddleOuterLayers", static_cast(trkPars.overallGeom)); + break; + } // Middle layers mLayers[0].setLayout(trkPars.layoutML); @@ -157,7 +178,7 @@ void Detector::configFromFile(std::string fileName) while (getline(ss, substr, '\t')) { tmpBuff.push_back(std::stof(substr)); } - mLayers.emplace_back(layerCount, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount)}, tmpBuff[0], tmpBuff[1], tmpBuff[2]); + mLayers.emplace_back(layerCount, GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount), tmpBuff[0], tmpBuff[1], tmpBuff[2]); ++layerCount; } } @@ -238,13 +259,58 @@ void Detector::createGeometry() // Add service for inner tracker mServices.createServices(vTRK); - mPetalCases.clear(); - // Add petal cases (the sensitive layers inside the petal cases get constructed here too) + + // Build the VD using the petal builder + // Choose the VD design based on TRKBaseParam.layoutVD auto& trkPars = TRKBaseParam::Instance(); - for (Int_t petalCaseNumber = 0; petalCaseNumber < 4; ++petalCaseNumber) { - mPetalCases.emplace_back(petalCaseNumber, vTRK, trkPars.irisOpen); - mServices.excavateFromVacuum(mPetalCases[petalCaseNumber].getFullName()); + + o2::trk::clearVDSensorRegistry(); + + switch (trkPars.layoutVD) { + case kIRIS4: + LOG(info) << "Building VD with IRIS4 layout"; + o2::trk::createIRIS4Geometry(vTRK); + break; + case kIRISFullCyl: + LOG(info) << "Building VD with IRIS fully cylindrical layout"; + o2::trk::createIRISGeometryFullCyl(vTRK); + break; + case kIRIS5: + LOG(info) << "Building VD with IRIS5 layout"; + o2::trk::createIRIS5Geometry(vTRK); + break; + case kIRIS4a: + LOG(info) << "Building VD with IRIS4a layout"; + o2::trk::createIRIS4aGeometry(vTRK); + break; + default: + LOG(fatal) << "Unknown VD layout option: " << static_cast(trkPars.layoutVD); + break; + } + + // Fill sensor names from registry right after geometry creation + const auto& regs = o2::trk::vdSensorRegistry(); + mNumberOfVolumesVD = static_cast(regs.size()); + mNumberOfVolumes = mNumberOfVolumesVD + mLayers.size(); + mSensorName.resize(mNumberOfVolumes); + + // Fill VD sensor names from registry + int VDvolume = 0; + for (const auto& sensor : regs) { + mSensorName[VDvolume] = sensor.name; + VDvolume++; + } + + // Add MLOT sensor names + for (int i = 0; i < mLayers.size(); i++) { + mSensorName[VDvolume++].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i); + } + + for (auto vd : mSensorName) { + std::cout << "Volume name: " << vd << std::endl; } + + mServices.excavateFromVacuum("IRIS_CUTOUTsh"); mServices.registerVacuum(vTRK); } @@ -253,6 +319,12 @@ void Detector::InitializeO2Detector() LOG(info) << "Initialize TRK O2Detector"; mGeometryTGeo = GeometryTGeo::Instance(); defineSensitiveVolumes(); + + mSensorID.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different namingh scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12 + for (int i = 0; i < mNumberOfVolumes; i++) { + mSensorID[i] = gMC ? TVirtualMC::GetMC()->VolId(mSensorName[i]) : 0; // Volume ID from the Geant geometry + LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data()); + } } void Detector::defineSensitiveVolumes() @@ -263,26 +335,18 @@ void Detector::defineSensitiveVolumes() TString volumeName; LOGP(info, "Adding TRK Sensitive Volumes"); - // Add petal case sensitive volumes - for (int petalCase = 0; petalCase < 4; ++petalCase) { - // Petal layers - for (int petalLayer = 0; petalLayer < mPetalCases[petalCase].mPetalLayers.size(); ++petalLayer) { - volumeName = mPetalCases[petalCase].mPetalLayers[petalLayer].getSensorName(); - if (petalLayer == 0) { - mFirstOrLastLayers.push_back(volumeName.Data()); - } - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + // Register VD sensors created by VDGeometryBuilder + for (const auto& s : o2::trk::vdSensorRegistry()) { + TGeoVolume* v = gGeoManager->GetVolume(s.name.c_str()); + if (!v) { + LOGP(warning, "VD sensor volume '{}' not found", s.name); + continue; } - // Petal disks - for (int petalDisk = 0; petalDisk < mPetalCases[petalCase].mPetalDisks.size(); ++petalDisk) { - volumeName = mPetalCases[petalCase].mPetalDisks[petalDisk].getSensorName(); - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + LOGP(info, "Adding VD Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + // Optionally track first/last layers for TR references: + if (s.region == o2::trk::VDSensorDesc::Region::Barrel && (s.idx == 0 /*innermost*/)) { + mFirstOrLastLayers.push_back(s.name); } } @@ -338,9 +402,27 @@ bool Detector::ProcessHits(FairVolume* vol) return false; } - int lay = vol->getVolumeId(); + int subDetID = -1; + int layer = -1; + int volume = 0; int volID = vol->getMCid(); + bool notSens = false; + while ((volume < mNumberOfVolumes) && (notSens = (volID != mSensorID[volume]))) { + ++volume; /// there are 44 volumes, 36 for the VD (1 for each sensing element) and 8 for the MLOT (1 for each layer) + } + + if (notSens) { + return kFALSE; // RS: can this happen? This method must be called for sensors only? + } + + if (volume < mNumberOfVolumesVD) { + subDetID = 0; // VD. For the moment each "chip" is a volume./// TODO: change this logic once the naming scheme is changed + } else { + subDetID = 1; // MLOT + layer = volume - mNumberOfVolumesVD; + } + // Is it needed to keep a track reference when the outer ITS volume is encountered? auto stack = (o2::data::Stack*)fMC->GetStack(); // if (fMC->IsTrackExiting() && (lay == 0 || lay == mLayers.size() - 1)) { @@ -348,7 +430,7 @@ bool Detector::ProcessHits(FairVolume* vol) // Keep the track refs for the innermost and outermost layers only o2::TrackReference tr(*fMC, GetDetId()); tr.setTrackID(stack->GetCurrentTrackNumber()); - tr.setUserId(lay); + tr.setUserId(volume); stack->addTrackReference(tr); } bool startHit = false, stopHit = false; @@ -398,13 +480,27 @@ bool Detector::ProcessHits(FairVolume* vol) TLorentzVector positionStop; fMC->TrackPosition(positionStop); // Retrieve the indices with the volume path - int stave(0), halfstave(0), chipinmodule(0), module; - fMC->CurrentVolOffID(1, chipinmodule); - fMC->CurrentVolOffID(2, module); - fMC->CurrentVolOffID(3, halfstave); - fMC->CurrentVolOffID(4, stave); + int stave(0), halfstave(0), mod(0), chip(0); + if (subDetID == 1) { + fMC->CurrentVolOffID(1, chip); + fMC->CurrentVolOffID(2, mod); + if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { + fMC->CurrentVolOffID(3, halfstave); + fMC->CurrentVolOffID(4, stave); + } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { + fMC->CurrentVolOffID(3, stave); + } else { + LOGP(fatal, "Wrong number of halfstaves for layer {}", layer); + } + } /// if VD, for the moment the volume is the "chipID" so no need to retrieve other elments + + unsigned short chipID = mGeometryTGeo->getChipIndex(subDetID, volume, layer, stave, halfstave, mod, chip); - Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + // Print(vol, volume, subDetID, layer, stave, halfstave, mod, chip, chipID); + + // mGeometryTGeo->Print(); + + Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(), mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); // p->SetTotalEnergy(vmc->Etot()); @@ -417,13 +513,34 @@ bool Detector::ProcessHits(FairVolume* vol) return true; } -o2::itsmft::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) +o2::trk::Hit* Detector::addHit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos, + const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus, + unsigned char endStatus) { mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus); return &(mHits->back()); } + +void Detector::Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int mod, int chip, int chipID) const +{ + int currentVol(0); + LOG(info) << "Current volume name: " << fMC->CurrentVolName() << " and ID " << fMC->CurrentVolID(currentVol); + LOG(info) << "volume: " << volume << "/" << mNumberOfVolumes - 1; + LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip; + LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " module: " << mod; + if (subDetID == 1 && mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { // staggered geometry + LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " halfstave: " << halfstave; + LOG(info) << "off volume name 4 " << fMC->CurrentVolOffName(4) << " stave: " << stave; + LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID; + } else if (subDetID == 1 && mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { // turbo geometry + LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " stave: " << stave; + LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID; + } else { + LOG(info) << "SubDetector ID: " << subDetID << " Chip ID: " << chipID; + } + LOG(info); +} + } // namespace trk } // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx new file mode 100644 index 0000000000000..e2a78702204e5 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx @@ -0,0 +1,84 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DigiParams.cxx +/// \brief Implementation of the TRK digitization steering params. Based on the ITS2 code. + +#include +#include "Framework/Logger.h" +#include "TRKSimulation/DigiParams.h" +#include "TRKSimulation/ChipSimResponse.h" + +using namespace o2::trk; + +DigiParams::DigiParams() +{ + // make sure the defaults are consistent + setNSimSteps(mNSimSteps); +} + +void DigiParams::setROFrameLength(float lNS) +{ + // set ROFrame length in nanosecongs + mROFrameLength = lNS; + assert(mROFrameLength > 1.); + mROFrameLengthInv = 1. / mROFrameLength; +} + +void DigiParams::setNSimSteps(int v) +{ + // set number of sampling steps in silicon + mNSimSteps = v > 0 ? v : 1; + mNSimStepsInv = 1.f / mNSimSteps; +} + +void DigiParams::setChargeThreshold(int v, float frac2Account) +{ + // set charge threshold for digits creation and its fraction to account + // contribution from single hit + mChargeThreshold = v; + mMinChargeToAccount = v * frac2Account; + if (mMinChargeToAccount < 0 || mMinChargeToAccount > mChargeThreshold) { + mMinChargeToAccount = mChargeThreshold; + } + LOG(info) << "Set charge threshold to " << mChargeThreshold + << ", single hit will be accounted from " << mMinChargeToAccount + << " electrons"; +} + +//______________________________________________ +void DigiParams::print() const +{ + // print settings + printf("TRK digitization params:\n"); + printf("Continuous readout : %s\n", mIsContinuous ? "ON" : "OFF"); + printf("Readout Frame Length(ns) : %f\n", mROFrameLength); + printf("Strobe delay (ns) : %f\n", mStrobeDelay); + printf("Strobe length (ns) : %f\n", mStrobeLength); + printf("Threshold (N electrons) : %d\n", mChargeThreshold); + printf("Min N electrons to account : %d\n", mMinChargeToAccount); + printf("Number of charge sharing steps : %d\n", mNSimSteps); + printf("ELoss to N electrons factor : %e\n", mEnergyToNElectrons); + printf("Noise level per pixel : %e\n", mNoisePerPixel); + printf("Charge time-response:\n"); + mSignalShape.print(); +} + +void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) +{ + LOG(debug) << "Response function data path: " << resp->getDataPath(); + LOG(debug) << "Response function info: "; + // resp->print(); + if (!resp) { + LOGP(fatal, "cannot set response function from null"); + } + mAlpSimResponse = std::make_unique(resp); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 21e6e629ec418..0fd8c7820ce28 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -12,456 +12,552 @@ /// \file Digitizer.cxx #include "DataFormatsITSMFT/Digit.h" -// #include "ITSMFTBase/SegmentationAlpide.h" +#include "TRKBase/SegmentationChip.h" #include "TRKSimulation/DPLDigitizerParam.h" +#include "TRKSimulation/TRKLayer.h" #include "TRKSimulation/Digitizer.h" -// #include "MathUtils/Cartesian.h" -// #include "SimulationDataFormat/MCTruthContainer.h" -// #include "DetectorsRaw/HBFUtils.h" +#include "DetectorsRaw/HBFUtils.h" -// #include +#include // #include -// #include -// #include +#include +#include +#include #include // for LOG using o2::itsmft::Digit; -using o2::itsmft::Hit; -// using Segmentation = o2::itsmft::SegmentationAlpide; +using o2::trk::Hit; +using Segmentation = o2::trk::SegmentationChip; using namespace o2::trk; +using namespace o2::itsmft; // using namespace o2::base; - //_______________________________________________________________________ void Digitizer::init() { - // mNumberOfChips = mGeometry->getNumberOfChips(); - // mChips.resize(mNumberOfChips); - // for (int i = mNumberOfChips; i--;) { - // mChips[i].setChipIndex(i); - // if (mNoiseMap) { - // mChips[i].setNoiseMap(mNoiseMap); - // } - // if (mDeadChanMap) { - // mChips[i].disable(mDeadChanMap->isFullChipMasked(i)); - // mChips[i].setDeadChanMap(mDeadChanMap); - // } - // } - // initializing for both collection tables - /*for (int i = 0; i < 2; i++) { - mAlpSimResp[i].initData(i); - }*/ - - // importing the charge collection tables - // (initialized while building O2) - // auto file = TFile::Open(mResponseFile.data()); - // if (!file) { - // LOG(fatal) << "Cannot open response file " << mResponseFile; - // } - /*std::string response = "response"; - for (int i=0; i<2; i++) { - response.append(std::to_string(i)); - mAlpSimResp[i] = *(o2::itsmft::AlpideSimResponse*)file->Get(response.data()); - }*/ - // mAlpSimResp[0] = *(o2::itsmft::AlpideSimResponse*)file->Get("response0"); - // mAlpSimResp[1] = *(o2::itsmft::AlpideSimResponse*)file->Get("response1"); + LOG(info) << "Initializing digitizer"; + mNumberOfChips = mGeometry->getNumberOfChips(); + mChips.resize(mNumberOfChips); /// temporary, to not make it crash + for (int i = mNumberOfChips; i--;) { + mChips[i].setChipIndex(i); + if (mNoiseMap) { + mChips[i].setNoiseMap(mNoiseMap); + } + if (mDeadChanMap) { + mChips[i].disable(mDeadChanMap->isFullChipMasked(i)); + mChips[i].setDeadChanMap(mDeadChanMap); + } + } + + // setting the correct response function (for the moment, for both VD and MLOT the same response function is used) + mChipSimResp = mParams.getAlpSimResponse(); + mChipSimRespVD = mChipSimResp; /// for the moment considering the same response + mChipSimRespMLOT = mChipSimResp; /// for the moment considering the same response + + /// setting scale factors to adapt to the APTS response function (adjusting pitch and Y shift) + // TODO: adjust Y shift when the geometry is improved + LOG(info) << " Depth max VD: " << mChipSimRespVD->getDepthMax(); + LOG(info) << " Depth min VD: " << mChipSimRespVD->getDepthMin(); + + LOG(info) << " Depth max MLOT: " << mChipSimRespMLOT->getDepthMax(); + LOG(info) << " Depth min MLOT: " << mChipSimRespMLOT->getDepthMin(); + + float thicknessVD = 0.0095; // cm --- hardcoded based on geometry currently present + float thicknessMLOT = o2::trk::SegmentationChip::SiliconThicknessMLOT; // 0.01 cm = 100 um --- based on geometry currently present + + LOG(info) << "Using response name: " << mRespName; + + if (mRespName == "APTS") { // default + mSimRespVDScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowVD; + mSimRespVDScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColVD; + mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -45. Must add ~10 um (= max depth) to match the APTS response. + mSimRespMLOTScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; + mSimRespMLOTScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; + } else if (mRespName == "ALICE3") { + mSimRespVDScaleX = o2::trk::constants::alice3resp::pitchX / o2::trk::SegmentationChip::PitchRowVD; + mSimRespVDScaleZ = o2::trk::constants::alice3resp::pitchZ / o2::trk::SegmentationChip::PitchColVD; + mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -95 um. Must align the start of epi layer with the response function. + mSimRespMLOTScaleX = o2::trk::constants::alice3resp::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; + mSimRespMLOTScaleZ = o2::trk::constants::alice3resp::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; + } else { + LOG(fatal) << "Unknown response name: " << mRespName; + } + + mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response + mSimRespOrientation = false; // importing the parameters from DPLDigitizerParam.h auto& dOptTRK = DPLDigitizerParam::Instance(); - LOGP(info, "TRK Digitizer is initalised."); + LOGP(info, "TRK Digitizer is initialised."); + mParams.print(); + LOGP(info, "VD shift = {} ; ML/OT shift = {} = {} - {}", mSimRespVDShift, mSimRespMLOTShift, mChipSimRespMLOT->getDepthMax(), thicknessMLOT / 2.f); + LOGP(info, "VD pixel scale on x = {} ; z = {}", mSimRespVDScaleX, mSimRespVDScaleZ); + LOGP(info, "ML/OT pixel scale on x = {} ; z = {}", mSimRespMLOTScaleX, mSimRespMLOTScaleZ); + LOGP(info, "Response orientation: {}", mSimRespOrientation ? "flipped" : "normal"); + + mIRFirstSampledTF = o2::raw::HBFUtils::Instance().getFirstSampledTFIR(); } -// auto Digitizer::getChipResponse(int chipID) -// { -// if (mNumberOfChips < 10000) { // in MFT -// return mAlpSimRespMFT; -// } +const o2::trk::ChipSimResponse* Digitizer::getChipResponse(int chipID) +{ + if (mGeometry->getSubDetID(chipID) == 0) { /// VD + return mChipSimRespVD; + } -// if (chipID < 432) { // in ITS Inner Barrel -// return mAlpSimRespIB; -// } else { // in ITS Outter Barrel -// return mAlpSimRespOB; -// } -// } + else if (mGeometry->getSubDetID(chipID) == 1) { /// ML/OT + return mChipSimRespMLOT; + } + return nullptr; +}; //_______________________________________________________________________ void Digitizer::process(const std::vector* hits, int evID, int srcID) { // digitize single event, the time must have been set beforehand - // LOG(info) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " - // << srcID << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" - // << " cont.mode: " << isContinuous() - // << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; + LOG(debug) << " Digitizing " << mGeometry->getName() << " (ID: " << mGeometry->getDetID() + << ") hits of entry " << evID << " from source " << srcID + << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" + << " cont.mode: " << isContinuous() + << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; + + std::cout << "Printing segmentation info: " << std::endl; + SegmentationChip::Print(); // // is there something to flush ? - // if (mNewROFrame > mROFrameMin) { - // fillOutputContainer(mNewROFrame - 1); // flush out all frame preceding the new one - // } - - // int nHits = hits->size(); - // std::vector hitIdx(nHits); - // std::iota(std::begin(hitIdx), std::end(hitIdx), 0); - // // sort hits to improve memory access - // std::sort(hitIdx.begin(), hitIdx.end(), - // [hits](auto lhs, auto rhs) { - // return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID(); - // }); - // for (int i : hitIdx) { - // processHit((*hits)[i], mROFrameMax, evID, srcID); - // } - // // in the triggered mode store digits after every MC event - // // TODO: in the real triggered mode this will not be needed, this is actually for the - // // single event processing only - // if (!mParams.isContinuous()) { - // fillOutputContainer(mROFrameMax); - // } + if (mNewROFrame > mROFrameMin) { + fillOutputContainer(mNewROFrame - 1); // flush out all frames preceding the new one + } + + int nHits = hits->size(); + std::vector hitIdx(nHits); + std::iota(std::begin(hitIdx), std::end(hitIdx), 0); + // sort hits to improve memory access + std::sort(hitIdx.begin(), hitIdx.end(), + [hits](auto lhs, auto rhs) { + return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID(); + }); + LOG(info) << "Processing " << nHits << " hits"; + for (int i : hitIdx) { + processHit((*hits)[i], mROFrameMax, evID, srcID); + } + + // in the triggered mode store digits after every MC event + // TODO: in the real triggered mode this will not be needed, this is actually for the + // single event processing only + if (!mParams.isContinuous()) { + fillOutputContainer(mROFrameMax); + } } //_______________________________________________________________________ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) { - // // assign event time in ns - // mEventTime = irt; - // if (!mParams.isContinuous()) { - // mROFrameMin = 0; // in triggered mode reset the frame counters - // mROFrameMax = 0; - // } - // // RO frame corresponding to provided time - // mCollisionTimeWrtROF = mEventTime.timeInBCNS; // in triggered mode the ROF starts at BC (is there a delay?) - // if (mParams.isContinuous()) { - // auto nbc = mEventTime.differenceInBC(mIRFirstSampledTF); - // if (mCollisionTimeWrtROF < 0 && nbc > 0) { - // nbc--; - // } - - // // we might get interactions to digitize from before - // // the first sampled IR - // if (nbc < 0) { - // mNewROFrame = 0; - // // this event is before the first RO - // mIsBeforeFirstRO = true; - // } else { - // mNewROFrame = nbc / mParams.getROFrameLengthInBC(); - // mIsBeforeFirstRO = false; - // } - // LOG(info) << " NewROFrame " << mNewROFrame << " nbc " << nbc; - - // // in continuous mode depends on starts of periodic readout frame - // mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS; - // } else { - // mNewROFrame = 0; - // } - - // if (mNewROFrame < mROFrameMin) { - // LOG(error) << "New ROFrame " << mNewROFrame << " (" << irt << ") precedes currently cashed " << mROFrameMin; - // throw std::runtime_error("deduced ROFrame precedes already processed one"); - // } - - // if (mParams.isContinuous() && mROFrameMax < mNewROFrame) { - // mROFrameMax = mNewROFrame - 1; // all frames up to this are finished - // } + LOG(info) << "Setting event time "; + // assign event time in ns + mEventTime = irt; + if (!mParams.isContinuous()) { + mROFrameMin = 0; // in triggered mode reset the frame counters + mROFrameMax = 0; + } + // RO frame corresponding to provided time + mCollisionTimeWrtROF = mEventTime.timeInBCNS; // in triggered mode the ROF starts at BC (is there a delay?) + if (mParams.isContinuous()) { + auto nbc = mEventTime.differenceInBC(mIRFirstSampledTF); + + if (mCollisionTimeWrtROF < 0 && nbc > 0) { + nbc--; + } + + mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + + LOG(debug) << " NewROFrame " << mNewROFrame << " = " << nbc << "/" << mParams.getROFrameLengthInBC() << " (nbc/mParams.getROFrameLengthInBC()"; + + // in continuous mode depends on starts of periodic readout frame + mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS; + } else { + mNewROFrame = 0; + } + + if (mNewROFrame < mROFrameMin) { + LOG(error) << "New ROFrame " << mNewROFrame << " (" << irt << ") precedes currently cashed " << mROFrameMin; + throw std::runtime_error("deduced ROFrame precedes already processed one"); + } + + if (mParams.isContinuous() && mROFrameMax < mNewROFrame) { + mROFrameMax = mNewROFrame - 1; // all frames up to this are finished + } } //_______________________________________________________________________ void Digitizer::fillOutputContainer(uint32_t frameLast) { // // fill output with digits from min.cached up to requested frame, generating the noise beforehand - // if (frameLast > mROFrameMax) { - // frameLast = mROFrameMax; - // } + if (frameLast > mROFrameMax) { + frameLast = mROFrameMax; + } // // make sure all buffers for extra digits are created up to the maxFrame - // getExtraDigBuffer(mROFrameMax); - - // LOG(info) << "Filling " << mGeometry->getName() << " digits output for RO frames " << mROFrameMin << ":" - // << frameLast; - - // o2::itsmft::ROFRecord rcROF; - - // // we have to write chips in RO increasing order, therefore have to loop over the frames here - // for (; mROFrameMin <= frameLast; mROFrameMin++) { - // rcROF.setROFrame(mROFrameMin); - // rcROF.setFirstEntry(mDigits->size()); // start of current ROF in digits - - // auto& extra = *(mExtraBuff.front().get()); - // for (auto& chip : mChips) { - // if (chip.isDisabled()) { - // continue; - // } - // chip.addNoise(mROFrameMin, mROFrameMin, &mParams); - // auto& buffer = chip.getPreDigits(); - // if (buffer.empty()) { - // continue; - // } - // auto itBeg = buffer.begin(); - // auto iter = itBeg; - // ULong64_t maxKey = chip.getOrderingKey(mROFrameMin + 1, 0, 0) - 1; // fetch digits with key below that - // for (; iter != buffer.end(); ++iter) { - // if (iter->first > maxKey) { - // break; // is the digit ROFrame from the key > the max requested frame - // } - // auto& preDig = iter->second; // preDigit - // if (preDig.charge >= mParams.getChargeThreshold()) { - // int digID = mDigits->size(); - // mDigits->emplace_back(chip.getChipIndex(), preDig.row, preDig.col, preDig.charge); - // mMCLabels->addElement(digID, preDig.labelRef.label); - // auto& nextRef = preDig.labelRef; // extra contributors are in extra array - // while (nextRef.next >= 0) { - // nextRef = extra[nextRef.next]; - // mMCLabels->addElement(digID, nextRef.label); - // } - // } - // } - // buffer.erase(itBeg, iter); - // } - // // finalize ROF record - // rcROF.setNEntries(mDigits->size() - rcROF.getFirstEntry()); // number of digits - // if (isContinuous()) { - // rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC()); - // } else { - // rcROF.getBCData() = mEventTime; // RSTODO do we need to add trigger delay? - // } - // if (mROFRecords) { - // mROFRecords->push_back(rcROF); - // } - // extra.clear(); // clear container for extra digits of the mROFrameMin ROFrame - // // and move it as a new slot in the end - // mExtraBuff.emplace_back(mExtraBuff.front().release()); - // mExtraBuff.pop_front(); - // } + getExtraDigBuffer(mROFrameMax); + LOG(info) << "Filling " << mGeometry->getName() << " digits output for RO frames " << mROFrameMin << ":" + << frameLast; + + o2::itsmft::ROFRecord rcROF; /// using temporarly itsmft::ROFRecord + + // we have to write chips in RO increasing order, therefore have to loop over the frames here + for (; mROFrameMin <= frameLast; mROFrameMin++) { + rcROF.setROFrame(mROFrameMin); + rcROF.setFirstEntry(mDigits->size()); // start of current ROF in digits + + auto& extra = *(mExtraBuff.front().get()); + for (auto& chip : mChips) { + if (chip.isDisabled()) { + continue; + } + // chip.addNoise(mROFrameMin, mROFrameMin, &mParams); /// TODO: add noise + auto& buffer = chip.getPreDigits(); + if (buffer.empty()) { + continue; + } + auto itBeg = buffer.begin(); + auto iter = itBeg; + ULong64_t maxKey = chip.getOrderingKey(mROFrameMin + 1, 0, 0) - 1; // fetch digits with key below that + for (; iter != buffer.end(); ++iter) { + if (iter->first > maxKey) { + break; // is the digit ROFrame from the key > the max requested frame + } + auto& preDig = iter->second; // preDigit + if (preDig.charge >= mParams.getChargeThreshold()) { + int digID = mDigits->size(); + mDigits->emplace_back(chip.getChipIndex(), preDig.row, preDig.col, preDig.charge); + LOG(debug) << "Adding digit ID: " << digID << " with chipID: " << chip.getChipIndex() << ", row: " << preDig.row << ", col: " << preDig.col << ", charge: " << preDig.charge; + mMCLabels->addElement(digID, preDig.labelRef.label); + auto& nextRef = preDig.labelRef; // extra contributors are in extra array + while (nextRef.next >= 0) { + nextRef = extra[nextRef.next]; + mMCLabels->addElement(digID, nextRef.label); + } + } + } + buffer.erase(itBeg, iter); + } + // finalize ROF record + rcROF.setNEntries(mDigits->size() - rcROF.getFirstEntry()); // number of digits + if (isContinuous()) { + rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC()); + } else { + rcROF.getBCData() = mEventTime; // RSTODO do we need to add trigger delay? + } + if (mROFRecords) { + mROFRecords->push_back(rcROF); + } + extra.clear(); // clear container for extra digits of the mROFrameMin ROFrame + // and move it as a new slot in the end + mExtraBuff.emplace_back(mExtraBuff.front().release()); + mExtraBuff.pop_front(); + } } //_______________________________________________________________________ -void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) +void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, int srcID) { - // // convert single hit to digits - // int chipID = hit.GetDetectorID(); - // auto& chip = mChips[chipID]; - // if (chip.isDisabled()) { - // LOG(debug) << "skip disabled chip " << chipID; - // return; - // } - // float timeInROF = hit.GetTime() * sec2ns; - // if (timeInROF > 20e3) { - // const int maxWarn = 10; - // static int warnNo = 0; - // if (warnNo < maxWarn) { - // LOG(warning) << "Ignoring hit with time_in_event = " << timeInROF << " ns" - // << ((++warnNo < maxWarn) ? "" : " (suppressing further warnings)"); - // } - // return; - // } - // if (isContinuous()) { - // timeInROF += mCollisionTimeWrtROF; - // } - // if (mIsBeforeFirstRO && timeInROF < 0) { - // // disregard this hit because it comes from an event before readout starts and it does not effect this RO - // return; - // } - - // // calculate RO Frame for this hit - // if (timeInROF < 0) { - // timeInROF = 0.; - // } - // float tTot = mParams.getSignalShape().getMaxDuration(); - // // frame of the hit signal start wrt event ROFrame - // int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv()); - // // frame of the hit signal end wrt event ROFrame: in the triggered mode we read just 1 frame - // uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv() : roFrameRel; - // int nFrames = roFrameRelMax + 1 - roFrameRel; - // uint32_t roFrameMax = mNewROFrame + roFrameRelMax; - // if (roFrameMax > maxFr) { - // maxFr = roFrameMax; // if signal extends beyond current maxFrame, increase the latter - // } - - // // here we start stepping in the depth of the sensor to generate charge diffusion - // float nStepsInv = mParams.getNSimStepsInv(); - // int nSteps = mParams.getNSimSteps(); - // const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID()); - // math_utils::Vector3D xyzLocS(matrix ^ (hit.GetPosStart())); // start position in sensor frame - // math_utils::Vector3D xyzLocE(matrix ^ (hit.GetPos())); // end position in sensor frame - - // math_utils::Vector3D step(xyzLocE); - // step -= xyzLocS; - // step *= nStepsInv; // position increment at each step - // // the electrons will injected in the middle of each step - // math_utils::Vector3D stepH(step * 0.5); - // xyzLocS += stepH; - // xyzLocE -= stepH; - - // int rowS = -1, colS = -1, rowE = -1, colE = -1, nSkip = 0; - // // get entrance pixel row and col - // while (!Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ? - // if (++nSkip >= nSteps) { - // return; // did not enter to sensitive matrix - // } - // xyzLocS += step; - // } - // // get exit pixel row and col - // while (!Segmentation::localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ? - // if (++nSkip >= nSteps) { - // return; // did not enter to sensitive matrix - // } - // xyzLocE -= step; - // } - // // estimate the limiting min/max row and col where the non-0 response is possible - // if (rowS > rowE) { - // std::swap(rowS, rowE); - // } - // if (colS > colE) { - // std::swap(colS, colE); - // } - // rowS -= AlpideRespSimMat::NPix / 2; - // rowE += AlpideRespSimMat::NPix / 2; - // if (rowS < 0) { - // rowS = 0; - // } - // if (rowE >= Segmentation::NRows) { - // rowE = Segmentation::NRows - 1; - // } - // colS -= AlpideRespSimMat::NPix / 2; - // colE += AlpideRespSimMat::NPix / 2; - // if (colS < 0) { - // colS = 0; - // } - // if (colE >= Segmentation::NCols) { - // colE = Segmentation::NCols - 1; - // } - // int rowSpan = rowE - rowS + 1, colSpan = colE - colS + 1; // size of plaquet where some response is expected - - // float respMatrix[rowSpan][colSpan]; // response accumulated here - // std::fill(&respMatrix[0][0], &respMatrix[0][0] + rowSpan * colSpan, 0.f); - - // float nElectrons = hit.GetEnergyLoss() * mParams.getEnergyToNElectrons(); // total number of deposited electrons - // nElectrons *= nStepsInv; // N electrons injected per step - // if (nSkip) { - // nSteps -= nSkip; - // } - // // - // int rowPrev = -1, colPrev = -1, row, col; - // float cRowPix = 0.f, cColPix = 0.f; // local coordinated of the current pixel center - - // const o2::itsmft::AlpideSimResponse* resp = getChipResponse(chipID); - - // // take into account that the AlpideSimResponse depth defintion has different min/max boundaries - // // although the max should coincide with the surface of the epitaxial layer, which in the chip - // // local coordinates has Y = +SensorLayerThickness/2 - - // xyzLocS.SetY(xyzLocS.Y() + resp->getDepthMax() - Segmentation::SensorLayerThickness / 2.); - - // // collect charge in every pixel which might be affected by the hit - // for (int iStep = nSteps; iStep--;) { - // // Get the pixel ID - // Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col); - // if (row != rowPrev || col != colPrev) { // update pixel and coordinates of its center - // if (!Segmentation::detectorToLocal(row, col, cRowPix, cColPix)) { - // continue; // should not happen - // } - // rowPrev = row; - // colPrev = col; - // } - // bool flipCol, flipRow; - // // note that response needs coordinates along column row (locX) (locZ) then depth (locY) - // auto rspmat = resp->getResponse(xyzLocS.X() - cRowPix, xyzLocS.Z() - cColPix, xyzLocS.Y(), flipRow, flipCol); - - // xyzLocS += step; - // if (!rspmat) { - // continue; - // } - - // for (int irow = AlpideRespSimMat::NPix; irow--;) { - // int rowDest = row + irow - AlpideRespSimMat::NPix / 2 - rowS; // destination row in the respMatrix - // if (rowDest < 0 || rowDest >= rowSpan) { - // continue; - // } - // for (int icol = AlpideRespSimMat::NPix; icol--;) { - // int colDest = col + icol - AlpideRespSimMat::NPix / 2 - colS; // destination column in the respMatrix - // if (colDest < 0 || colDest >= colSpan) { - // continue; - // } - // respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, flipRow, flipCol); - // } - // } - // } - - // // fire the pixels assuming Poisson(n_response_electrons) - // o2::MCCompLabel lbl(hit.GetTrackID(), evID, srcID, false); - // auto roFrameAbs = mNewROFrame + roFrameRel; - // for (int irow = rowSpan; irow--;) { - // uint16_t rowIS = irow + rowS; - // for (int icol = colSpan; icol--;) { - // float nEleResp = respMatrix[irow][icol]; - // if (!nEleResp) { - // continue; - // } - // int nEle = gRandom->Poisson(nElectrons * nEleResp); // total charge in given pixel - // // ignore charge which have no chance to fire the pixel - // if (nEle < mParams.getMinChargeToAccount()) { - // continue; - // } - // uint16_t colIS = icol + colS; - // if (mNoiseMap && mNoiseMap->isNoisy(chipID, rowIS, colIS)) { - // continue; - // } - // if (mDeadChanMap && mDeadChanMap->isNoisy(chipID, rowIS, colIS)) { - // continue; - // } - // // - // registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl); - // } - // } + int chipID = hit.GetDetectorID(); //// the chip ID at the moment is not referred to the chip but to a wider detector element (e.g. quarter of layer or disk in VD, stave in ML, half stave in OT) + int subDetID = mGeometry->getSubDetID(chipID); + + int layer = mGeometry->getLayer(chipID); + int disk = mGeometry->getDisk(chipID); + + if (disk != -1) { + LOG(debug) << "Skipping disk " << disk; + return; // skipping hits on disks for the moment + } + + LOG(debug) << "Processing hit for chip " << chipID; + auto& chip = mChips[chipID]; + if (chip.isDisabled()) { + LOG(debug) << "Skipping disabled chip " << chipID; + return; + } + float timeInROF = hit.GetTime() * sec2ns; + LOG(debug) << "timeInROF: " << timeInROF; + if (timeInROF > 20e3) { + const int maxWarn = 10; + static int warnNo = 0; + if (warnNo < maxWarn) { + LOG(warning) << "Ignoring hit with time_in_event = " << timeInROF << " ns" + << ((++warnNo < maxWarn) ? "" : " (suppressing further warnings)"); + } + return; + } + if (isContinuous()) { + timeInROF += mCollisionTimeWrtROF; + } + if (timeInROF < 0) { + // disregard this hit because it comes from an event byefore readout starts and it does not effect this RO + LOG(debug) << "Ignoring hit with timeInROF = " << timeInROF; + return; + } + + // calculate RO Frame for this hit + if (timeInROF < 0) { + timeInROF = 0.; + } + float tTot = mParams.getSignalShape().getMaxDuration(); + // frame of the hit signal start wrt event ROFrame + int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv()); + // frame of the hit signal end wrt event ROFrame: in the triggered mode we read just 1 frame + uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv() : roFrameRel; + int nFrames = roFrameRelMax + 1 - roFrameRel; + uint32_t roFrameMax = mNewROFrame + roFrameRelMax; + if (roFrameMax > maxFr) { + maxFr = roFrameMax; // if signal extends beyond current maxFrame, increase the latter + } + + // here we start stepping in the depth of the sensor to generate charge diffusion + float nStepsInv = mParams.getNSimStepsInv(); + int nSteps = mParams.getNSimSteps(); + + const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID()); + // matrix.print(); + + /// transorm from the global detector coordinates to the local detector coordinates + math_utils::Vector3D xyzLocS(matrix ^ (hit.GetPosStart())); // start position in sensor frame + math_utils::Vector3D xyzLocE(matrix ^ (hit.GetPos())); // end position in sensor frame + + if (subDetID == 0) { // VD - need to take into account for the curved layers. TODO: consider the disks + // transform the point on the curved surface to a flat one + math_utils::Vector2D xyFlatS = Segmentation::curvedToFlat(layer, xyzLocS.x(), xyzLocS.y()); + math_utils::Vector2D xyFlatE = Segmentation::curvedToFlat(layer, xyzLocE.x(), xyzLocE.y()); + LOG(debug) << "Called curved to flat: " << xyzLocS.x() << " -> " << xyFlatS.x() << ", " << xyzLocS.y() << " -> " << xyFlatS.y(); + // update the local coordinates with the flattened ones + xyzLocS.SetXYZ(xyFlatS.x(), xyFlatS.y(), xyzLocS.Z()); + xyzLocE.SetXYZ(xyFlatE.x(), xyFlatE.y(), xyzLocE.Z()); + } + + // std::cout<<"Printing example of point in 0.35 0.35 0 in global frame: "< examplehitGlob(0.35, 0.35, 0); + // math_utils::Vector3D exampleLoc(matrix ^ (examplehitGlob)); // start position in sensor frame + // std::cout<< "Example hit in local frame: " << exampleLoc << std::endl; + // std::cout<<"Going back to glob coordinates: " << (matrix * exampleLoc) << std::endl; + + math_utils::Vector3D step(xyzLocE); + step -= xyzLocS; + step *= nStepsInv; // position increment at each step + // the electrons will injected in the middle of each step + // starting from the middle of the first step + math_utils::Vector3D stepH(step * 0.5); + xyzLocS += stepH; + xyzLocE -= stepH; + + LOG(debug) << "Step into the sensitive volume: " << step << ". Number of steps: " << nSteps; + int rowS = -1, colS = -1, rowE = -1, colE = -1, nSkip = 0; + + /// here it is the control whether the hit is in the sensitive matrix based on the segmentation + // get entrance pixel row and col + while (!Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS, subDetID, layer, disk)) { // guard-ring ? + if (++nSkip >= nSteps) { + LOG(debug) << "Did not enter to sensitive matrix, " << nSkip << " >= " << nSteps; + return; // did not enter to sensitive matrix + } + xyzLocS += step; + } + + // get exit pixel row and col + while (!Segmentation::localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE, subDetID, layer, disk)) { /// for the moment chipID = bigger element + if (++nSkip >= nSteps) { + LOG(debug) << "Did not enter to sensitive matrix, " << nSkip << " >= " << nSteps; + return; // did not enter to sensitive matrix + } + xyzLocE -= step; + } + + int nCols = getNCols(subDetID, layer); + int nRows = getNRows(subDetID, layer); + + // estimate the limiting min/max row and col where the non-0 response is possible + if (rowS > rowE) { + std::swap(rowS, rowE); + } + if (colS > colE) { + std::swap(colS, colE); + } + rowS -= AlpideRespSimMat::NPix / 2; + rowE += AlpideRespSimMat::NPix / 2; + if (rowS < 0) { + rowS = 0; + } + if (rowE >= nRows) { + rowE = nRows - 1; + } + colS -= AlpideRespSimMat::NPix / 2; + colE += AlpideRespSimMat::NPix / 2; + if (colS < 0) { + colS = 0; + } + if (colE >= nCols) { + colE = nCols - 1; + } + int rowSpan = rowE - rowS + 1, colSpan = colE - colS + 1; // size of plaquet where some response is expected + + float respMatrix[rowSpan][colSpan]; // response accumulated here + std::fill(&respMatrix[0][0], &respMatrix[0][0] + rowSpan * colSpan, 0.f); + + float nElectrons = hit.GetEnergyLoss() * mParams.getEnergyToNElectrons(); // total number of deposited electrons + nElectrons *= nStepsInv; // N electrons injected per step + if (nSkip) { + nSteps -= nSkip; + } + + int rowPrev = -1, colPrev = -1, row, col; + float cRowPix = 0.f, cColPix = 0.f; // local coordinate of the current pixel center + + const o2::trk::ChipSimResponse* resp = getChipResponse(chipID); + // std::cout << "Printing chip response:" << std::endl; + // resp->print(); + + // take into account that the ChipSimResponse depth defintion has different min/max boundaries + // although the max should coincide with the surface of the epitaxial layer, which in the chip + // local coordinates has Y = +SensorLayerThickness/2 + // LOG(info)<<"SubdetID = " << subDetID<< " shift: "<print(); // print the response matrix for debugging + + for (int irow = AlpideRespSimMat::NPix; irow--;) { + int rowDest = row + irow - AlpideRespSimMat::NPix / 2 - rowS; // destination row in the respMatrix + if (rowDest < 0 || rowDest >= rowSpan) { + continue; + } + for (int icol = AlpideRespSimMat::NPix; icol--;) { + int colDest = col + icol - AlpideRespSimMat::NPix / 2 - colS; // destination column in the respMatrix + if (colDest < 0 || colDest >= colSpan) { + continue; + } + respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, !flipCol); + } + } + } + + // fire the pixels assuming Poisson(n_response_electrons) + o2::MCCompLabel lbl(hit.GetTrackID(), evID, srcID, false); + auto roFrameAbs = mNewROFrame + roFrameRel; + LOG(debug) << "\nSpanning through rows and columns; rowspan = " << rowSpan << " colspan = " << colSpan << " = " << colE << " - " << colS << " +1 "; + for (int irow = rowSpan; irow--;) { // irow ranging from 4 to 0 + uint16_t rowIS = irow + rowS; // row distant irow from the row of the hit start + for (int icol = colSpan; icol--;) { // icol ranging from 4 to 0 + float nEleResp = respMatrix[irow][icol]; // value of the probability of the response in this pixel + if (nEleResp <= 1.e-36) { + continue; + } + LOG(debug) << "nEleResp: value " << nEleResp << " for pixel " << irow << " " << icol; + int nEle = gRandom->Poisson(nElectrons * nEleResp); // total charge in given pixel = number of electrons generated in the hit multiplied by the probability of being detected in their position + LOG(debug) << "Charge detected in the pixel: " << nEle << " for pixel " << irow << " " << icol; + // ignore charge which have no chance to fire the pixel + if (nEle < mParams.getMinChargeToAccount()) { /// TODO: substitute with the threshold? + LOG(debug) << "Ignoring pixel with nEle = " << nEle << " < min charge to account " + << mParams.getMinChargeToAccount() << " for pixel " << irow << " " << icol; + continue; + } + + uint16_t colIS = icol + colS; // col distant icol from the col of the hit start + if (mNoiseMap && mNoiseMap->isNoisy(chipID, rowIS, colIS)) { + continue; + } + if (mDeadChanMap && mDeadChanMap->isNoisy(chipID, rowIS, colIS)) { + continue; + } + + registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl); + } + } } //________________________________________________________________________________ -void Digitizer::registerDigits(o2::itsmft::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, +void Digitizer::registerDigits(o2::trk::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl) { // Register digits for given pixel, accounting for the possible signal contribution to // multiple ROFrame. The signal starts at time tInROF wrt the start of provided roFrame // In every ROFrame we check the collected signal during strobe - - // float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start - // for (int i = 0; i < nROF; i++) { - // uint32_t roFr = roFrame + i; - // int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength()); - // tStrobe += mParams.getROFrameLength(); // for the next ROF - - // // discard too small contributions, they have no chance to produce a digit - // if (nEleROF < mParams.getMinChargeToAccount()) { - // continue; - // } - // if (roFr > mEventROFrameMax) { - // mEventROFrameMax = roFr; - // } - // if (roFr < mEventROFrameMin) { - // mEventROFrameMin = roFr; - // } - // auto key = chip.getOrderingKey(roFr, row, col); - // PreDigit* pd = chip.findDigit(key); - // if (!pd) { - // chip.addDigit(key, roFr, row, col, nEleROF, lbl); - // } else { // there is already a digit at this slot, account as PreDigitExtra contribution - // pd->charge += nEleROF; - // if (pd->labelRef.label == lbl) { // don't store the same label twice - // continue; - // } - // ExtraDig* extra = getExtraDigBuffer(roFr); - // int& nxt = pd->labelRef.next; - // bool skip = false; - // while (nxt >= 0) { - // if ((*extra)[nxt].label == lbl) { // don't store the same label twice - // skip = true; - // break; - // } - // nxt = (*extra)[nxt].next; - // } - // if (skip) { - // continue; - // } - // // new predigit will be added in the end of the chain - // nxt = extra->size(); - // extra->emplace_back(lbl); - // } - // } + LOG(debug) << "Registering digits for chip " << chip.getChipIndex() << " at ROFrame " << roFrame + << " row " << row << " col " << col << " nEle " << nEle << " label " << lbl; + float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start + for (int i = 0; i < nROF; i++) { + uint32_t roFr = roFrame + i; + int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength()); + tStrobe += mParams.getROFrameLength(); // for the next ROF + + // discard too small contributions, they have no chance to produce a digit + if (nEleROF < mParams.getMinChargeToAccount()) { /// use threshold instead? + continue; + } + if (roFr > mEventROFrameMax) { + mEventROFrameMax = roFr; + } + if (roFr < mEventROFrameMin) { + mEventROFrameMin = roFr; + } + auto key = chip.getOrderingKey(roFr, row, col); + o2::itsmft::PreDigit* pd = chip.findDigit(key); + if (!pd) { + chip.addDigit(key, roFr, row, col, nEleROF, lbl); + LOG(debug) << "Added digit " << key << " " << roFr << " " << row << " " << col << " " << nEleROF; + } else { // there is already a digit at this slot, account as PreDigitExtra contribution + pd->charge += nEleROF; + if (pd->labelRef.label == lbl) { // don't store the same label twice + continue; + } + ExtraDig* extra = getExtraDigBuffer(roFr); + int& nxt = pd->labelRef.next; + bool skip = false; + while (nxt >= 0) { + if ((*extra)[nxt].label == lbl) { // don't store the same label twice + skip = true; + break; + } + nxt = (*extra)[nxt].next; + } + if (skip) { + continue; + } + // new predigit will be added in the end of the chain + nxt = extra->size(); + extra->emplace_back(lbl); + } + } } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx new file mode 100644 index 0000000000000..fe496bc59692f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.cxx +/// \brief Implementation of the Hit class + +#include "TRKSimulation/Hit.h" + +#include +#include + +ClassImp(o2::trk::Hit); + +using std::cout; +using std::endl; +using namespace o2::trk; +using namespace o2; //::base; + +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()); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index a95418afbba25..82b6fbd40af59 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -11,6 +11,7 @@ #include "TRKSimulation/TRKLayer.h" #include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include "Framework/Logger.h" @@ -24,37 +25,34 @@ namespace o2 { namespace trk { -TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, float zLength, float layerX2X0) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mZ(zLength), mX2X0(layerX2X0), mModuleWidth(4.54), mLayout(kCylinder) +TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, int numberOfModules, float layerX2X0) + : mLayerNumber(layerNumber), mLayout(kCylinder), mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mNumberOfModules(numberOfModules), mX2X0(layerX2X0), mChipWidth(constants::moduleMLOT::chip::width), mChipLength(constants::moduleMLOT::chip::length), mDeadzoneWidth(constants::moduleMLOT::chip::passiveEdgeReadOut), mSensorThickness(constants::moduleMLOT::silicon::thickness), mHalfNumberOfChips(4) { float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, mZ, mX2X0); + LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); } -TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, float zLength, float thick) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mZ(zLength), mChipThickness(thick), mModuleWidth(4.54), mLayout(kCylinder) +TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thick) + : mLayerNumber(layerNumber), mLayout(kCylinder), mLayerName(layerName), mInnerRadius(rInn), mNumberOfModules(numberOfModules), mChipThickness(thick), mChipWidth(constants::moduleMLOT::chip::width), mChipLength(constants::moduleMLOT::chip::length), mDeadzoneWidth(constants::moduleMLOT::chip::passiveEdgeReadOut), mSensorThickness(constants::moduleMLOT::silicon::thickness), mHalfNumberOfChips(4) { float Si_X0 = 9.5f; mOuterRadius = rInn + thick; mX2X0 = mChipThickness / Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, mZ, mX2X0); + LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); } -TGeoVolume* TRKLayer::createSensor(std::string type, double width) +TGeoVolume* TRKLayer::createSensor(std::string type) { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - std::string sensName = Form("%s%d", GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + std::string sensName = GeometryTGeo::getTRKSensorPattern() + std::to_string(mLayerNumber); TGeoShape* sensor; if (type == "cylinder") { - sensor = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2); + sensor = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); // TO BE CHECKED !!! } else if (type == "flat") { - if (width < 0) { - LOGP(fatal, "Attempting to create sensor with invalid width"); - } - sensor = new TGeoBBox(width / 2, mChipThickness / 2, mZ / 2); + sensor = new TGeoBBox((mChipWidth - mDeadzoneWidth) / 2, mSensorThickness / 2, mChipLength / 2); // TO BE CHECKED !!! } else { LOGP(fatal, "Sensor of type '{}' is not implemented", type); } @@ -65,75 +63,263 @@ TGeoVolume* TRKLayer::createSensor(std::string type, double width) return sensVol; }; -TGeoVolume* TRKLayer::createChip(std::string type, double width) +TGeoVolume* TRKLayer::createDeadzone(std::string type) +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string deadName = GeometryTGeo::getTRKDeadzonePattern() + std::to_string(mLayerNumber); + + TGeoShape* deadzone; + + if (type == "cylinder") { + deadzone = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, 0); // TO BE CHECKED !!! + } else if (type == "flat") { + deadzone = new TGeoBBox(mDeadzoneWidth / 2, mSensorThickness / 2, mChipLength / 2); // TO BE CHECKED !!! + } else { + LOGP(fatal, "Deadzone of type '{}' is not implemented", type); + } + + TGeoVolume* deadVol = new TGeoVolume(deadName.c_str(), deadzone, medSi); + deadVol->SetLineColor(kGray); + + return deadVol; +}; + +TGeoVolume* TRKLayer::createMetalStack(std::string type) +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string metalName = GeometryTGeo::getTRKMetalStackPattern() + std::to_string(mLayerNumber); + + TGeoShape* metalStack; + + if (type == "cylinder") { + metalStack = new TGeoTube(mInnerRadius + mSensorThickness, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); // TO BE CHECKED !!! + } else if (type == "flat") { + metalStack = new TGeoBBox(mChipWidth / 2, (mChipThickness - mSensorThickness) / 2, mChipLength / 2); // TO BE CHECKED !!! + } else { + LOGP(fatal, "Metal stack of type '{}' is not implemented", type); + } + + TGeoVolume* metalVol = new TGeoVolume(metalName.c_str(), metalStack, medSi); + metalVol->SetLineColor(kGray); + + return metalVol; +}; + +TGeoVolume* TRKLayer::createChip(std::string type) { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - std::string chipName = o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber); + std::string chipName = GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber); TGeoShape* chip; + TGeoVolume* chipVol; + TGeoVolume* sensVol; + TGeoVolume* deadVol; + TGeoVolume* metalVol; if (type == "cylinder") { - chip = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2); + chip = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); + sensVol = createSensor("cylinder"); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + metalVol = createMetalStack("cylinder"); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, nullptr); + + // deadVol = createDeadzone("cylinder"); } else if (type == "flat") { - if (width < 0) { - LOGP(fatal, "Attempting to create chip with invalid width"); - } - chip = new TGeoBBox(width / 2, mChipThickness / 2, mZ / 2); - sensVol = createSensor("flat", width); + chip = new TGeoBBox(mChipWidth / 2, mChipThickness / 2, mChipLength / 2); // TO BE CHECKED !!! + chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); + + sensVol = createSensor("flat"); + deadVol = createDeadzone("flat"); + metalVol = createMetalStack("flat"); + + TGeoCombiTrans* transSens = new TGeoCombiTrans(); + transSens->SetTranslation(-mDeadzoneWidth / 2, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + + TGeoCombiTrans* transDead = new TGeoCombiTrans(); + transDead->SetTranslation((mChipWidth - mDeadzoneWidth) / 2, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! + LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); + chipVol->AddNode(deadVol, 1, transDead); + + TGeoCombiTrans* transMetal = new TGeoCombiTrans(); + transMetal->SetTranslation(0, mSensorThickness / 2, 0); // TO BE CHECKED !!! + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, transMetal); } else { LOGP(fatal, "Sensor of type '{}' is not implemented", type); } - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); chipVol->SetLineColor(kYellow); return chipVol; } -TGeoVolume* TRKLayer::createStave(std::string type, double width) +TGeoVolume* TRKLayer::createModule(std::string type) +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string moduleName = GeometryTGeo::getTRKModulePattern() + std::to_string(mLayerNumber); + + TGeoShape* module; + TGeoVolume* moduleVol; + + if (type == "cylinder") { + double moduleLength = constants::moduleMLOT::length * mNumberOfModules; + + module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, moduleLength / 2); + moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); + + TGeoVolume* chipVol = createChip("cylinder"); + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVol, 1, nullptr); + } else if (type == "flat") { + double moduleWidth = constants::moduleMLOT::width; + double moduleLength = constants::moduleMLOT::length; + + module = new TGeoBBox(moduleWidth / 2, mChipThickness / 2, moduleLength / 2); // TO BE CHECKED !!! + moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); + + for (int iChip = 0; iChip < mHalfNumberOfChips; iChip++) { + TGeoVolume* chipVolLeft = createChip("flat"); + TGeoVolume* chipVolRight = createChip("flat"); + + // Put the chips in the correct position + double xLeft = -moduleWidth / 2 + constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::width / 2; + double zLeft = -moduleLength / 2 + constants::moduleMLOT::gaps::outerEdgeShortSide + iChip * (constants::moduleMLOT::chip::length + constants::moduleMLOT::gaps::interChips) + constants::moduleMLOT::chip::length / 2; + + TGeoCombiTrans* transLeft = new TGeoCombiTrans(); + transLeft->SetTranslation(xLeft, 0, zLeft); // TO BE CHECKED !!! + TGeoRotation* rot = new TGeoRotation(); + rot->RotateY(180); + transLeft->SetRotation(rot); + LOGP(debug, "Inserting {} in {} ", chipVolLeft->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVolLeft, iChip * 2, transLeft); + + double xRight = +moduleWidth / 2 - constants::moduleMLOT::gaps::outerEdgeLongSide - constants::moduleMLOT::chip::width / 2; + double zRight = -moduleLength / 2 + constants::moduleMLOT::gaps::outerEdgeShortSide + iChip * (constants::moduleMLOT::chip::length + constants::moduleMLOT::gaps::interChips) + constants::moduleMLOT::chip::length / 2; + + TGeoCombiTrans* transRight = new TGeoCombiTrans(); + transRight->SetTranslation(xRight, 0, zRight); // TO BE CHECKED !!! + LOGP(debug, "Inserting {} in {} ", chipVolRight->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVolRight, iChip * 2 + 1, transRight); + } + } else { + LOGP(fatal, "Chip of type '{}' is not implemented", type); + } + + moduleVol->SetLineColor(kYellow); + + return moduleVol; +} + +TGeoVolume* TRKLayer::createHalfStave(std::string type) +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); + + TGeoShape* halfStave; + TGeoVolume* halfStaveVol; + + double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; + + if (type == "cylinder") { + halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, halfStaveLength / 2); + halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); + + TGeoVolume* moduleVol = createModule("cylinder"); + LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); + halfStaveVol->AddNode(moduleVol, 1, nullptr); + } else if (type == "flat") { + double moduleLength = constants::moduleMLOT::length; + double halfStaveWidth = constants::OT::halfstave::width; + + halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); + halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); + + for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + TGeoVolume* moduleVol = createModule("flat"); + + // Put the modules in the correct position + double zPos = -0.5 * mNumberOfModules * moduleLength + (iModule + 0.5) * moduleLength; + + TGeoCombiTrans* trans = new TGeoCombiTrans(); + trans->SetTranslation(0, 0, zPos); // TO BE CHECKED !!! + + LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); + halfStaveVol->AddNode(moduleVol, iModule, trans); + } + } + + halfStaveVol->SetLineColor(kYellow); + + return halfStaveVol; +} + +TGeoVolume* TRKLayer::createStave(std::string type) { TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - std::string staveName = o2::trk::GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber); + std::string staveName = GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber); TGeoShape* stave; TGeoVolume* staveVol; - TGeoVolume* chipVol; + + double staveLength = constants::moduleMLOT::length * mNumberOfModules; if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2); - chipVol = createChip("cylinder"); + stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVol, 1, nullptr); + + TGeoVolume* moduleVol = createModule("cylinder"); + LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), staveVol->GetName()); + staveVol->AddNode(moduleVol, 1, nullptr); } else if (type == "flat") { - if (width < 0) { - LOGP(fatal, "Attempting to create stave with invalid width"); - } - stave = new TGeoBBox(width / 2, mChipThickness / 2, mZ / 2); - chipVol = createChip("flat", width); + double moduleLength = constants::moduleMLOT::length; + double staveWidth = constants::ML::width; + + stave = new TGeoBBox(staveWidth / 2, mChipThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVol, 1, nullptr); + + for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + TGeoVolume* moduleVol = createModule("flat"); + + // Put the modules in the correct position + double zPos = -0.5 * mNumberOfModules * moduleLength + (iModule + 0.5) * moduleLength; + + TGeoCombiTrans* trans = new TGeoCombiTrans(); + trans->SetTranslation(0, 0, zPos); // TO BE CHECKED !!! + + LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), staveVol->GetName()); + staveVol->AddNode(moduleVol, iModule, trans); + } } else if (type == "staggered") { - double width = mModuleWidth * 2; // Each stave has two modules (based on the LOI design) - stave = new TGeoBBox(width / 2, mLogicalVolumeThickness / 2, mZ / 2); - TGeoVolume* chipVolLeft = createChip("flat", mModuleWidth); - TGeoVolume* chipVolRight = createChip("flat", mModuleWidth); + double overlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap + double shift = overlap / 2; + + double halfstaveWidth = constants::OT::halfstave::width; + double staveWidth = constants::OT::width - overlap; + + stave = new TGeoBBox(staveWidth / 2, mLogicalVolumeThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); + // Put the half staves in the correct position + TGeoVolume* halfStaveVolLeft = createHalfStave("flat"); + TGeoVolume* halfStaveVolRight = createHalfStave("flat"); + TGeoCombiTrans* transLeft = new TGeoCombiTrans(); - transLeft->SetTranslation(-mModuleWidth / 2 + 0.05, 0, 0); // 1mm overlap between the modules - LOGP(info, "Inserting {} in {} ", chipVolLeft->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVolLeft, 0, transLeft); + transLeft->SetTranslation(-halfstaveWidth / 2 + shift, 0, 0); // TO BE CHECKED !!! 1mm overlap between the modules + LOGP(debug, "Inserting {} in {} ", halfStaveVolLeft->GetName(), staveVol->GetName()); + staveVol->AddNode(halfStaveVolLeft, 0, transLeft); TGeoCombiTrans* transRight = new TGeoCombiTrans(); - transRight->SetTranslation(mModuleWidth / 2 - 0.05, 0.2, 0); - LOGP(info, "Inserting {} in {} ", chipVolRight->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVolRight, 1, transRight); + transRight->SetTranslation(halfstaveWidth / 2 - shift, 0.2, 0); // TO BE CHECKED !!! 1mm overlap between the modules + LOGP(debug, "Inserting {} in {} ", halfStaveVolRight->GetName(), staveVol->GetName()); + staveVol->AddNode(halfStaveVolRight, 1, transRight); } else { LOGP(fatal, "Chip of type '{}' is not implemented", type); } @@ -145,70 +331,79 @@ TGeoVolume* TRKLayer::createStave(std::string type, double width) void TRKLayer::createLayer(TGeoVolume* motherVolume) { - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - std::string staveName = o2::trk::GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber), - chipName = o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber), - sensName = Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mLayerNumber); - double layerThickness = mChipThickness; if (mLayout != eLayout::kCylinder) { layerThickness = mLogicalVolumeThickness; } - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, mZ / 2); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - layerVol->SetLineColor(kYellow); + TGeoTube* layer; + TGeoVolume* layerVol; + + double layerLength = constants::moduleMLOT::length * mNumberOfModules; if (mLayout == eLayout::kCylinder) { - auto staveVol = createStave("cylinder"); - LOGP(info, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); + layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, layerLength / 2); + layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + + TGeoVolume* staveVol = createStave("cylinder"); + LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, 1, nullptr); } else if (mLayout == eLayout::kTurboStaves) { - // Compute the number of staves - double width = mModuleWidth; // Each stave has two modules (based on the LOI design) + double staveWidth = constants::ML::width; // Each stave has two modules (based on the LOI design) + if (mInnerRadius > 25) { - width *= 2; // Outer layers have two modules per stave + staveWidth = constants::OT::width; // Outer layers have two modules per stave } - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / width); + layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); + layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + + // Compute the number of staves + int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / staveWidth); nStaves += nStaves % 2; // Require an even number of staves // Compute the size of the overlap region double theta = 2 * TMath::Pi() / nStaves; - double theta1 = std::atan(width / 2 / mInnerRadius); + double theta1 = std::atan(staveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); - double theta2 = std::atan((mInnerRadius * st - width / 2 * ct) / (mInnerRadius * ct + width / 2 * st)); + double theta2 = std::atan((mInnerRadius * st - staveWidth / 2 * ct) / (mInnerRadius * ct + staveWidth / 2 * st)); double overlap = (theta1 - theta2) * mInnerRadius; LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); for (int iStave = 0; iStave < nStaves; iStave++) { - TGeoVolume* staveVol = createStave("flat", width); + TGeoVolume* staveVol = createStave("flat"); // Put the staves in the correct position and orientation TGeoCombiTrans* trans = new TGeoCombiTrans(); double theta = 360. * iStave / nStaves; - TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 3, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 3, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); - LOGP(info, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); + LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } } else if (mLayout == kStaggered) { + double overlapInStave = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap + + double staveWidth = constants::OT::width - overlapInStave; + + layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); + layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + // Compute the number of staves - double width = mModuleWidth * 2; // Each stave has two modules (based on the LOI design) - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / width); + int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / staveWidth); nStaves += nStaves % 2; // Require an even number of staves // Compute the size of the overlap region double theta = 2 * TMath::Pi() / nStaves; - double theta1 = std::atan(width / 2 / mInnerRadius); + double theta1 = std::atan(staveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); - double theta2 = std::atan((mInnerRadius * st - width / 2 * ct) / (mInnerRadius * ct + width / 2 * st)); + double theta2 = std::atan((mInnerRadius * st - staveWidth / 2 * ct) / (mInnerRadius * ct + staveWidth / 2 * st)); double overlap = (theta1 - theta2) * mInnerRadius; LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); @@ -218,17 +413,19 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) // Put the staves in the correct position and orientation TGeoCombiTrans* trans = new TGeoCombiTrans(); double theta = 360. * iStave / nStaves; - TGeoRotation* rot = new TGeoRotation("rot", theta + 90, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 3, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); - LOGP(info, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); + LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } } else { LOGP(fatal, "Layout not implemented"); } - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + layerVol->SetLineColor(kYellow); + + LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); motherVolume->AddNode(layerVol, 1, nullptr); } // ClassImp(TRKLayer); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx deleted file mode 100644 index c729d7d1ec4dd..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/TRKPetalCase.h" -#include "TRKBase/GeometryTGeo.h" -#include - -#include "Framework/Logger.h" - -#include "TGeoTube.h" -#include "TGeoMatrix.h" -#include "TGeoCompositeShape.h" -#include "TGeoVolume.h" -#include "TString.h" -#include "TMath.h" - -namespace o2 -{ -namespace trk -{ -TRKPetalCase::TRKPetalCase(Int_t number, TGeoVolume* motherVolume, Bool_t irisOpen) : mPetalCaseNumber(number), mOpenState(irisOpen) -{ - - mWallThickness = .15e-1; // cm // Assume all the walls have the same thickness for now. - mRIn = 0.48; // cm - mROut = 3; // cm - mRInOpenState = 1.5; // cm - mPetalCaseLength = 70.; // cm - - // Calculate angular coverages of azimuthal part of wall (equivalent to that of the sensitive volumes) - mAngularCoverageAzimuthalWall = (0.25 * (2 * mRIn * TMath::Pi()) - 2 * mWallThickness) / mRIn; - mAngularCoverageRadialWall = mWallThickness / mRIn; - mToDeg = 180 / TMath::Pi(); - - // Calculate the center of the petal (x_c, y_c, z_c) based on whether it is open or not - mZPos = 0; - if (mOpenState) { - Double_t rHalfPetal = 0.5 * (mRIn + mROut); - Double_t rOpenStateCenter = TMath::Sqrt(rHalfPetal * rHalfPetal + mRInOpenState * mRInOpenState); - mXPos = rOpenStateCenter * TMath::Cos(0.25 * TMath::Pi() + (mPetalCaseNumber - 1) * 0.5 * TMath::Pi()); - mYPos = rOpenStateCenter * TMath::Sin(0.25 * TMath::Pi() + (mPetalCaseNumber - 1) * 0.5 * TMath::Pi()); - } else { - mXPos = 0.; - mYPos = 0.; - } - - // Make the petal case - constructCase(motherVolume); - // Make coldplate - constructColdPlate(motherVolume); - // Add the detection petals (quarter disks and barrel layers) - constructDetectionPetals(motherVolume); -} - -TString TRKPetalCase::getFullName() -{ - TString fullCompositeName = Form("PETALCASE%d_FULLCOMPOSITE", mPetalCaseNumber); - TGeoCompositeShape* fullCompositeShape = new TGeoCompositeShape(fullCompositeName, mFullCompositeFormula); - return fullCompositeName; -} - -void TRKPetalCase::constructCase(TGeoVolume* motherVolume) -{ - - // Petal case parts in TGeoTubeSeg - mInnerAzimuthalWall = new TGeoTubeSeg(Form("PETAL%d_INNER_AZIMUTHAL_WALL", mPetalCaseNumber), mRIn, mRIn + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - mOuterAzimuthalWall = new TGeoTubeSeg(Form("PETAL%d_OUTER_AZIMUTHAL_WALL", mPetalCaseNumber), mROut, mROut + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - mRadialWall = new TGeoTubeSeg(Form("PETAL%d_RADIAL_WALL", mPetalCaseNumber), mRIn, mROut + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageRadialWall * mToDeg, 0.5 * mAngularCoverageRadialWall * mToDeg); - mForwardWall = new TGeoTubeSeg(Form("PETAL%d_FORWARD_WALL", mPetalCaseNumber), mRIn, mROut + mWallThickness, mWallThickness / 2., -0.5 * (mAngularCoverageAzimuthalWall + 2 * mAngularCoverageRadialWall) * mToDeg, 0.5 * (mAngularCoverageAzimuthalWall + 2 * mAngularCoverageRadialWall) * mToDeg); - - // Rotate to correct section : 0-3 - mAzimuthalWallRot = new TGeoRotation((TString)Form("PETAL%d_AZIMUTHAL_WALL_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + 0.5 * mAngularCoverageAzimuthalWall + mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mAzimuthalWallRot->RegisterYourself(); - mRadialWall1Rot = new TGeoRotation((TString)Form("PETAL%d_RADIAL_WALL1_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + 0.5 * mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mRadialWall1Rot->RegisterYourself(); - mRadialWall2Rot = new TGeoRotation((TString)Form("PETAL%d_RADIAL_WALL2_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + mAngularCoverageAzimuthalWall + 1.5 * mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mRadialWall2Rot->RegisterYourself(); - - // Place to correct position (open or closed) - mAzimuthalWallCombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mAzimuthalWallRot); - mAzimuthalWallCombiTrans->RegisterYourself(); - mRadialWall1CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_RADIAL_WALL1_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mRadialWall1Rot); - mRadialWall1CombiTrans->RegisterYourself(); - mRadialWall2CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_RADIAL_WALL2_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mRadialWall2Rot); - mRadialWall2CombiTrans->RegisterYourself(); - mForwardWall1CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_FORWARD_WALL1_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, (mPetalCaseLength + mWallThickness) / 2., mAzimuthalWallRot); - mForwardWall1CombiTrans->RegisterYourself(); - mForwardWall2CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_FORWARD_WALL2_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, -(mPetalCaseLength + mWallThickness) / 2., mAzimuthalWallRot); - mForwardWall2CombiTrans->RegisterYourself(); - - TString petalCaseCompositeFormula = (TString)Form("PETAL%d_INNER_AZIMUTHAL_WALL:PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_OUTER_AZIMUTHAL_WALL:PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_RADIAL_WALL:PETAL%d_RADIAL_WALL1_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_RADIAL_WALL:PETAL%d_RADIAL_WALL2_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_FORWARD_WALL:PETAL%d_FORWARD_WALL1_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_FORWARD_WALL:PETAL%d_FORWARD_WALL2_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber); - - TGeoCompositeShape* petalCaseComposite = new TGeoCompositeShape((TString)Form("PETALCASE%dsh", mPetalCaseNumber), petalCaseCompositeFormula); - mFullCompositeFormula = petalCaseComposite->GetName(); - auto& matmgr = o2::base::MaterialManager::Instance(); - const TGeoMedium* kMedBe = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_BERYLLIUM"); - - mPetalCaseName = Form("PETALCASE%d", mPetalCaseNumber); - mPetalCaseVolume = new TGeoVolume(mPetalCaseName, petalCaseComposite, kMedBe); - mPetalCaseVolume->SetVisibility(1); - mPetalCaseVolume->SetLineColor(kGray); - - LOGP(info, "Creating IRIS Tracker vacuum petal case {}", mPetalCaseNumber); - LOGP(info, "Inserting {} in {} ", mPetalCaseVolume->GetName(), motherVolume->GetName()); - motherVolume->AddNode(mPetalCaseVolume, 1, nullptr); -} - -void TRKPetalCase::constructColdPlate(TGeoVolume* motherVolume) -{ - Double_t coldPlateRadius = 2.6; // cm - Double_t coldPlateThickness = 0.15; // cm - Double_t coldPlateLength = 50.; // cm - - mColdPlate = new TGeoTubeSeg((TString)Form("PETAL%d_COLDPLATE", mPetalCaseNumber), coldPlateRadius, coldPlateRadius + coldPlateThickness, coldPlateLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - auto& matmgr = o2::base::MaterialManager::Instance(); - const TGeoMedium* medCeramic = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CERAMIC"); - mColdPlateVolume = new TGeoVolume(Form("COLDPLATE%d", mPetalCaseNumber), mColdPlate, medCeramic); - - TString coldPlateCompositeFormula = mColdPlate->GetName(); - coldPlateCompositeFormula += ":"; - coldPlateCompositeFormula += mAzimuthalWallCombiTrans->GetName(); - addToPetalCaseComposite(coldPlateCompositeFormula); - - mColdPlateVolume->SetVisibility(1); - mColdPlateVolume->SetLineColor(kGray); - - LOGP(info, "Creating cold plate service"); - LOGP(info, "Inserting {} in {} ", mColdPlateVolume->GetName(), motherVolume->GetName()); - motherVolume->AddNode(mColdPlateVolume, 1, mAzimuthalWallCombiTrans); -} - -void TRKPetalCase::constructDetectionPetals(TGeoVolume* motherVolume) -{ - // Add petal layers - // layerNumber, layerName, rIn, angularCoverage, zLength, layerx2X0 - mPetalLayers.emplace_back(0, Form("%s_LAYER%d", mPetalCaseName.Data(), 0), 0.5f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - mPetalLayers.emplace_back(1, Form("%s_LAYER%d", mPetalCaseName.Data(), 1), 1.2f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - mPetalLayers.emplace_back(2, Form("%s_LAYER%d", mPetalCaseName.Data(), 2), 2.5f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - for (Int_t i = 0; i < mPetalLayers.size(); ++i) { - mPetalLayers[i].createLayer(motherVolume, mAzimuthalWallCombiTrans); - } - - // Add petal disks - // diskNumber, diskName, zPos, rIn, rOut, angularCoverage, diskx2X0 - mPetalDisks.emplace_back(0, Form("%s_DISK%d", mPetalCaseName.Data(), 0), 26., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(1, Form("%s_DISK%d", mPetalCaseName.Data(), 1), 30., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(2, Form("%s_DISK%d", mPetalCaseName.Data(), 2), 34., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(3, Form("%s_DISK%d", mPetalCaseName.Data(), 3), -26., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(4, Form("%s_DISK%d", mPetalCaseName.Data(), 4), -30., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(5, Form("%s_DISK%d", mPetalCaseName.Data(), 5), -34., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - for (Int_t i = 0; i < mPetalDisks.size(); ++i) { - mPetalDisks[i].createDisk(motherVolume, mAzimuthalWallCombiTrans); - } - - addDetectionPetelsToFullComposite(); -} - -void TRKPetalCase::addDetectionPetelsToFullComposite() -{ - for (Int_t i = 0; i < mPetalLayers.size(); ++i) { - Double_t zLength = mPetalLayers[i].getZLength(); - Double_t rIn = mPetalLayers[i].getInnerRadius(); - Double_t thickness = mPetalLayers[i].getChipThickness(); - Double_t angularCoverage = mPetalLayers[i].getAngularCoverage(); - TGeoTubeSeg* layerForExcavation = new TGeoTubeSeg(Form("PETALCASE%d_EXCAVATIONLAYER%d", mPetalCaseNumber, i), rIn, rIn + thickness, zLength / 2., -0.5 * angularCoverage * mToDeg, 0.5 * angularCoverage * mToDeg); - - TString layerForExcavationCompositeFormula = layerForExcavation->GetName(); - layerForExcavationCompositeFormula += ":"; - layerForExcavationCompositeFormula += mAzimuthalWallCombiTrans->GetName(); - addToPetalCaseComposite(layerForExcavationCompositeFormula); - } - - for (Int_t i = 0; i < mPetalDisks.size(); ++i) { - Double_t zPos = mPetalDisks[i].getZ(); - Double_t rIn = mPetalDisks[i].getInnerRadius(); - Double_t rOut = mPetalDisks[i].getOuterRadius(); - Double_t thickness = mPetalDisks[i].getThickness(); - Double_t angularCoverage = mPetalDisks[i].getAngularCoverage(); - TGeoTubeSeg* diskForExcavation = new TGeoTubeSeg(Form("PETALCASE%d_EXCAVATIONDISK%d", mPetalCaseNumber, i), rIn, rOut, thickness / 2., -0.5 * angularCoverage * mToDeg, 0.5 * angularCoverage * mToDeg); - TGeoCombiTrans* diskForExcavationCombiTrans = new TGeoCombiTrans(*(mAzimuthalWallCombiTrans->MakeClone())); // Copy from petal case - diskForExcavationCombiTrans->SetName((TString)Form("PETALCASE%d_EXCAVATIONDISK%d_COMBITRANS", mPetalCaseNumber, i)); - diskForExcavationCombiTrans->SetDz(zPos); // Overwrite z location - diskForExcavationCombiTrans->RegisterYourself(); - - TString diskForExcavationCompositeFormula = diskForExcavation->GetName(); - diskForExcavationCompositeFormula += ":"; - diskForExcavationCompositeFormula += diskForExcavationCombiTrans->GetName(); - addToPetalCaseComposite(diskForExcavationCompositeFormula); - } -} - -// ClassImp(TRKPetalCase); -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx deleted file mode 100644 index e24b24b48c882..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TRKPetalDisk.cxx -/// \brief Implementation of the TRKPetalDisk class - -#include "TRKSimulation/TRKPetalDisk.h" -#include "TRKBase/GeometryTGeo.h" - -#include // for LOG - -#include "TGeoManager.h" // for TGeoManager, gGeoManager -#include "TGeoMatrix.h" // for TGeoCombiTrans, TGeoRotation, etc -#include "TGeoTube.h" // for TGeoTube, TGeoTubeSeg -#include "TGeoVolume.h" // for TGeoVolume, TGeoVolumeAssembly -#include "TGeoCompositeShape.h" // for TGeoCompositeShape -#include "TMathBase.h" // for Abs -#include "TMath.h" // for Sin, RadToDeg, DegToRad, Cos, Tan, etc -#include "TGeoTube.h" - -#include // for snprintf - -namespace o2 -{ -namespace trk -{ - -TRKPetalDisk::TRKPetalDisk(Int_t diskNumber, std::string diskName, Float_t z, Float_t rIn, Float_t rOut, Float_t angularCoverage, Float_t Diskx2X0) -{ - // Creates a simple parametrized petal disk - mDiskNumber = diskNumber; - mDiskName = diskName; - mZ = z; - mAngularCoverage = angularCoverage; - mx2X0 = Diskx2X0; - mInnerRadius = rIn; - mOuterRadius = rOut; - Float_t Si_X0 = 9.5; - mChipThickness = Diskx2X0 * Si_X0; - - LOG(info) << "Creating TRK Disk " << mDiskNumber; - LOG(info) << " Using silicon X0 = " << Si_X0 << " to emulate disk radiation length."; - LOG(info) << " Disk z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; -} - -void TRKPetalDisk::createDisk(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans) -{ - // Create tube, set sensitive volume, add to mother volume - Double_t toDeg = 180 / TMath::Pi(); - std::string chipName = mDiskName + "_" + o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mDiskNumber), - sensName = mDiskName + "_" + Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mDiskNumber); - - mSensorName = sensName; - - TGeoTubeSeg* sensor = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* chip = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* disk = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - sensVol->SetLineColor(kYellow); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - chipVol->SetLineColor(kYellow); - TGeoVolume* diskVol = new TGeoVolume(mDiskName.c_str(), disk, medAir); - diskVol->SetLineColor(kYellow); - - LOG(info) << "Inserting " << sensVol->GetName() << " inside " << chipVol->GetName(); - chipVol->AddNode(sensVol, 1, nullptr); - - LOG(info) << "Inserting " << chipVol->GetName() << " inside " << diskVol->GetName(); - diskVol->AddNode(chipVol, 1, nullptr); - - // Finally put everything in the mother volume - TGeoCombiTrans* fwdPetalCombiTrans = new TGeoCombiTrans(*(combiTrans->MakeClone())); // Copy from petal case - fwdPetalCombiTrans->SetDz(mZ); // Overwrite z location - fwdPetalCombiTrans->RegisterYourself(); - - LOG(info) << "Inserting " << diskVol->GetName() << " inside " << motherVolume->GetName(); - motherVolume->AddNode(diskVol, 1, fwdPetalCombiTrans); -} -// ClassImp(TRKPetalLayer); - -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx deleted file mode 100644 index c8ff0d957bb19..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/TRKPetalLayer.h" -#include "TRKBase/GeometryTGeo.h" - -#include "Framework/Logger.h" - -#include "TGeoTube.h" -#include "TGeoBBox.h" -#include "TGeoVolume.h" -#include "TGeoTube.h" -#include "TGeoMatrix.h" - -#include "TMath.h" - -namespace o2 -{ -namespace trk -{ -TRKPetalLayer::TRKPetalLayer(Int_t layerNumber, std::string layerName, Float_t rIn, Float_t angularCoverage, Float_t zLength, Float_t layerX2X0) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rIn), mAngularCoverage(angularCoverage), mZ(zLength), mX2X0(layerX2X0), mModuleWidth(4.54) -{ - Float_t Si_X0 = 9.5f; - mChipThickness = mX2X0 * Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} thickness: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mChipThickness, mZ, mX2X0); -} - -void TRKPetalLayer::createLayer(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans) -{ - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - - std::string staveName = mLayerName + "_" + o2::trk::GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber), - chipName = mLayerName + "_" + o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber), - sensName = mLayerName + "_" + Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mLayerNumber); - - mSensorName = sensName; - - Double_t toDeg = 180 / TMath::Pi(); - mLayer = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), mLayer, medAir); - layerVol->SetLineColor(kYellow); - - TGeoTubeSeg* stave = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* chip = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* sensor = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - sensVol->SetLineColor(kYellow); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - chipVol->SetLineColor(kYellow); - TGeoVolume* staveVol = new TGeoVolume(staveName.c_str(), stave, medSi); - staveVol->SetLineColor(kYellow); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, combiTrans); -} -// ClassImp(TRKPetalLayer); - -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 1fb966425f974..bd27a5bc30f62 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -173,7 +173,9 @@ void TRKServices::registerVacuum(TGeoVolume* motherVolume) TGeoVolume* vacuumVolume = new TGeoVolume("A3IP_VACUUM", vacuumComposite, kMedVac); // Add the vacuum to the barrel - vacuumVolume->SetLineColor(kGreen - 3); + vacuumVolume->SetLineColor(kAzure + 7); + vacuumVolume->SetTransparency(80); + motherVolume->AddNode(vacuumVolume, 1, new TGeoTranslation(0, 0, 0)); } @@ -262,8 +264,8 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Carbon Fiber Cylinder support for the middle tracker float rMinMiddleCarbonSupport = 34.8f; // Arbitrary value float rMaxMiddleCarbonSupport = 35.f; // 2 mm of carbon fiber - const float zLengthMiddleCarbon = 62.f; - TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon); + const float zLengthMiddleCarbon = 129.f; + TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon / 2.); TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); middleBarrelCarbonSupportVolume->SetLineColor(kGray); LOGP(info, "Creating carbon fiber support for Middle Tracker"); @@ -316,28 +318,28 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Middle barrel connection disks const float rMinMiddleBarrelDisk = 5.68f; const float rMaxMiddleBarrelDisk = 35.f; - const float zLengthMiddleBarrel = 62.f; + const float zLengthMiddleBarrel = 64.5f; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { - TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick); - TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick); + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); + TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_SIO2_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskSIO2, medSiO2); TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPE, medPE); middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); middleBarrelConnDiskPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); - auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2 + zLengthMiddleBarrel), rot); - auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2 + zLengthMiddleBarrel), rot); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2. + zLengthMiddleBarrel), rot); motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarrelConnDiskPEVolume, 1, combiTransPE); - TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick); - TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick); + TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick / 2.); + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick / 2.); TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_CU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskCu, medCu); TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPEPower, medPE); middleBarrelConnDiskCuVolume->SetLineColor(kGray); middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); - auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2 + zLengthMiddleBarrel), rot); - auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2 + zLengthMiddleBarrel), rot); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2. + zLengthMiddleBarrel), rot); motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); @@ -355,39 +357,39 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) float rMaxMiddleServicesBarFwd = 74.5f + siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { // Create fibers: 3.07mm, 50% SiO2, 50% PE - TGeoTube* middleBarFwdFiberSIO2 = new TGeoTube("TRK_MIDBARFWD_FIBER_SIO2sh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, siO2FiberThick); - TGeoTube* middleBarFwdFiberPE = new TGeoTube("TRK_MIDBARFWD_FIBER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, peFiberThick); + TGeoTube* middleBarFwdFiberSIO2 = new TGeoTube("TRK_MIDBARFWD_FIBER_SIO2sh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, siO2FiberThick / 2.); + TGeoTube* middleBarFwdFiberPE = new TGeoTube("TRK_MIDBARFWD_FIBER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, peFiberThick / 2.); TGeoVolume* middleBarFwdFiberSIO2Volume = new TGeoVolume("TRK_MIDBARFWD_FIBER_SIO2", middleBarFwdFiberSIO2, medSiO2); TGeoVolume* middleBarFwdFiberPEVolume = new TGeoVolume("TRK_MIDBARFWD_FIBER_PE", middleBarFwdFiberPE, medPE); middleBarFwdFiberSIO2Volume->SetLineColor(kGray); middleBarFwdFiberPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); - auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2 + zLengthMiddleServices), rot); - auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2 + zLengthMiddleServices), rot); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2. + zLengthMiddleServices), rot); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdFiberSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarFwdFiberPEVolume, 1, combiTransPE); // Create powerlines: 10.9mm, 9% Cu, 91% PE - TGeoTube* middleBarFwdPowerCu = new TGeoTube("TRK_MIDBARFWD_POWER_CUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, cuPowerThick); - TGeoTube* middleBarFwdPowerPE = new TGeoTube("TRK_MIDBARFWD_POWER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, pePowerThick); + TGeoTube* middleBarFwdPowerCu = new TGeoTube("TRK_MIDBARFWD_POWER_CUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, cuPowerThick / 2.); + TGeoTube* middleBarFwdPowerPE = new TGeoTube("TRK_MIDBARFWD_POWER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, pePowerThick / 2.); TGeoVolume* middleBarFwdPowerCuVolume = new TGeoVolume("TRK_MIDBARFWD_POWER_CU", middleBarFwdPowerCu, medCu); TGeoVolume* middleBarFwdPowerPEVolume = new TGeoVolume("TRK_MIDBARFWD_POWER_PE", middleBarFwdPowerPE, medPE); middleBarFwdPowerCuVolume->SetLineColor(kGray); middleBarFwdPowerPEVolume->SetLineColor(kGray); - auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2 + zLengthMiddleServices), rot); - auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2 + zLengthMiddleServices), rot); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleServices), rot); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdPowerCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarFwdPowerPEVolume, 1, combiTransPEPower); // Create cooling pipes: 4.74mm, 56% PU, 44% H2O - TGeoTube* middleBarFwdCoolingPU = new TGeoTube("TRK_MIDBARFWD_COOLING_PUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, puCoolingThick); - TGeoTube* middleBarFwdCoolingH2O = new TGeoTube("TRK_MIDBARFWD_COOLING_H2Osh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, h2oCoolingThick); + TGeoTube* middleBarFwdCoolingPU = new TGeoTube("TRK_MIDBARFWD_COOLING_PUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, puCoolingThick / 2.); + TGeoTube* middleBarFwdCoolingH2O = new TGeoTube("TRK_MIDBARFWD_COOLING_H2Osh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, h2oCoolingThick / 2.); TGeoVolume* middleBarFwdCoolingPUVolume = new TGeoVolume("TRK_MIDBARFWD_COOLING_PU", middleBarFwdCoolingPU, medPU); TGeoVolume* middleBarFwdCoolingH2OVolume = new TGeoVolume("TRK_MIDBARFWD_COOLING_H2O", middleBarFwdCoolingH2O, medH2O); middleBarFwdCoolingPUVolume->SetLineColor(kGray); middleBarFwdCoolingH2OVolume->SetLineColor(kGray); - auto* combiTransCoolingPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2 + zLengthMiddleServices), rot); - auto* combiTransCoolingH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2 + zLengthMiddleServices), rot); + auto* combiTransCoolingPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2. + zLengthMiddleServices), rot); + auto* combiTransCoolingH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdCoolingPUVolume, 1, combiTransCoolingPU); motherVolume->AddNode(middleBarFwdCoolingH2OVolume, 1, combiTransCoolingH2O); } @@ -499,4 +501,4 @@ void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCoolingH2OVolume, 1, nullptr); } } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index d80027593cef0..fec9cb6631a6f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -15,14 +15,16 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::trk::TRKPetalCase + ; +#pragma link C++ class o2::trk::Hit + ; +#pragma link C++ class std::vector < o2::trk::Hit> + ; + #pragma link C++ class o2::trk::TRKLayer + ; -#pragma link C++ class o2::trk::TRKPetalLayer + ; -#pragma link C++ class o2::trk::TRKPetalDisk + ; +#pragma link C++ class o2::trk::VDLayer + ; #pragma link C++ class o2::trk::TRKServices + ; #pragma link C++ class o2::trk::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::trk::Detector> + ; #pragma link C++ class o2::trk::Digitizer + ; +#pragma link C++ class o2::trk::ChipSimResponse + ; #pragma link C++ class o2::trk::DPLDigitizerParam < o2::detectors::DetID::TRK> + ; #pragma link C++ class o2::trk::DPLDigitizerParam < o2::detectors::DetID::FT3> + ; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx new file mode 100644 index 0000000000000..b06faa38211bb --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -0,0 +1,943 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDGeometryBuilder.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TGeoManager.h" + +#include "Framework/Logger.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/VDLayer.h" +#include "TRKSimulation/VDSensorRegistry.h" + +namespace o2::trk +{ + +static std::vector gVDSensors; // stays in this TU only +std::vector& vdSensorRegistry() { return gVDSensors; } + +void clearVDSensorRegistry() { gVDSensors.clear(); } + +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx) +{ + gVDSensors.push_back({volName, petal, region, type, idx}); +} + +static inline std::string makeSensorName(const std::string& layerName, int layerNumber) +{ + return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getTRKSensorPattern(), layerNumber); +} + +namespace +{ + +// Config: which volumes count as SOLIDS to subtract from the vacuum volume +inline bool isSolidToCut(const TGeoVolume* v) +{ + const char* nm = v->GetName(); + const char* med = v->GetMedium() ? v->GetMedium()->GetName() : ""; + // silicon sensors (barrel + disks) + if (med && strcmp(med, "TRK_SILICON$") == 0) { + return true; + } + // walls, sidewalls, cold-plate, service rings (names from your builders) + if (TString(nm).BeginsWith("VD_InnerWallArc")) { + return true; + } + if (TString(nm).BeginsWith("VD_OuterWallArc")) { + return true; + } + if (TString(nm).BeginsWith("VD_SideWall")) { + return true; + } + if (TString(nm).BeginsWith("VD_InnerWallCyl")) { + return true; + } + if (TString(nm).BeginsWith("VD_OuterWallCyl")) { + return true; + } + if (TString(nm).Contains("_Coldplate")) { + return true; + } + if (TString(nm).BeginsWith("IRIS_Service_Neg")) { + return true; + } + if (TString(nm).BeginsWith("IRIS_Service_Pos_InVac")) { + return true; + } + return false; +} + +// Ensure every leaf shape has a stable, informative name +inline const char* ensureShapeName(TGeoVolume* v) +{ + auto* sh = v->GetShape(); + TString nm = sh->GetName(); + if (nm.IsNull() || nm.BeginsWith("TGeo")) { + TString wanted = TString(v->GetName()) + "_sh"; + // avoid collisions + int k = 0; + TString cand = wanted; + auto* shapes = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + while (shapes && shapes->FindObject(cand)) { + cand = Form("%s_%d", wanted.Data(), ++k); + } + sh->SetName(cand); + if (shapes && !shapes->FindObject(cand)) { + shapes->Add(sh); + } + } + return sh->GetName(); +} + +// Recorder state for the petal-local composite +static TString gPetalSolidsFormula; +static int gLocalTrIdx = 0; + +// add "ShapeName:IRIS_LOC_TR_k" to the petal-local formula (no outer rotation) +inline void appendLocalTerm(const char* shapeName, const TGeoHMatrix& H) +{ + auto* ct = new TGeoCombiTrans(H); + ct->SetName(Form("IRIS_LOC_TR_%d", gLocalTrIdx++)); + ct->RegisterYourself(); + if (!gPetalSolidsFormula.IsNull()) { + gPetalSolidsFormula += "+"; + } + gPetalSolidsFormula += TString::Format("%s:%s", shapeName, ct->GetName()); +} + +// DFS: compose LOCAL transforms only (identity prefix), to capture the petal contents +void traversePetalLocal(TGeoVolume* vol, const TGeoHMatrix& prefix) +{ + auto* nodes = vol->GetNodes(); + if (!nodes) { + return; + } + for (int i = 0; i < nodes->GetEntriesFast(); ++i) { + auto* node = (TGeoNode*)nodes->At(i); + auto* childV = node->GetVolume(); + TGeoHMatrix H(prefix); + if (auto* m = node->GetMatrix()) { + H.Multiply(m); + } + + if (isSolidToCut(childV)) { + const char* shapeName = ensureShapeName(childV); + appendLocalTerm(shapeName, H); + } + traversePetalLocal(childV, H); + } +} + +// Build (once) a petal-local composite containing ONLY solids (walls, silicon, coldplate, services, disks) +inline void buildPetalSolidsComposite(TGeoVolume* petalAsm) +{ + // If it already exists, skip + if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject("IRIS_PETAL_SOLIDSsh")) { + return; + } + + gPetalSolidsFormula.Clear(); + gLocalTrIdx = 0; + + TGeoHMatrix I; // identity + traversePetalLocal(petalAsm, I); + + if (gPetalSolidsFormula.IsNull()) { + LOGP(error, "IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal."); + return; + } + + LOGP(info, "IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data()); + new TGeoCompositeShape("IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data()); +} + +// Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase +inline void buildIrisCutoutFromPetalSolid(int nPetals) +{ + auto* shps = gGeoManager->GetListOfShapes(); + auto* base = shps ? dynamic_cast(shps->FindObject("IRIS_PETAL_SOLIDSsh")) : nullptr; + if (!base) { + LOGP(error, "IRIS cutout: shape 'IRIS_PETAL_SOLIDSsh' not found."); + return; + } + + // IMPORTANT: for nPetals==1, a composite expression like "A:tr" is invalid. + // Just clone the petal solids shape as the global cutout. + if (nPetals == 1) { + // Remove any previous shape with same name if it exists (optional but keeps things clean) + if (shps->FindObject("IRIS_CUTOUTsh")) { + // ROOT shape lists are owned by gGeoManager; removing is not always necessary. + // Keeping it simple: just create a unique name if it already exists. + LOGP(warning, "IRIS cutout: 'IRIS_CUTOUTsh' already exists; overwriting by clone name reuse may be unsafe."); + } + + auto* cut = dynamic_cast(base->Clone("IRIS_CUTOUTsh")); + if (!cut) { + LOGP(error, "IRIS cutout: failed to clone 'IRIS_PETAL_SOLIDSsh' to 'IRIS_CUTOUTsh'."); + return; + } + + LOGP(info, "IRIS_CUTOUTsh created as clone of IRIS_PETAL_SOLIDSsh (nPetals=1)."); + return; + } + + // nPetals > 1: build union of rotated copies + TString cutFormula; + for (int p = 0; p < nPetals; ++p) { + const double phi = (360.0 / nPetals) * (p + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phi); + auto* RT = new TGeoCombiTrans(0, 0, 0, R); + RT->SetName(Form("IRIS_PETAL_ROT_%d", p)); + RT->RegisterYourself(); + + if (p) { + cutFormula += "+"; + } + cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName()); + } + + LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); + auto* cut = new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); + (void)cut; + + // Stronger sanity: ensure it parsed into a boolean node + auto* cutCheck = dynamic_cast(shps->FindObject("IRIS_CUTOUTsh")); + if (!cutCheck || !cutCheck->GetBoolNode()) { + LOGP(error, "IRIS cutout sanity: IRIS_CUTOUTsh exists but parsing failed (no BoolNode)."); + } else { + LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); + } +} + +} // namespace + +// =================== Specs & constants (ROOT units: cm) =================== +static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer +static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length + +// Radii (cm) +static constexpr double rL0_cm = 0.5f; // 5 mm +static constexpr double rL1_cm = 1.2f; // 12 mm +static constexpr double rL2_cm = 2.5f; // 25 mm + +// IRIS5 rectangular L0 width (cm) +static constexpr double kL0RectHeight_cm = 0.5f; // 5.0 mm +static constexpr double kL0RectWidth_cm = 0.83f; // 8.3 mm + +// Disks radii (cm) +static constexpr double diskRin_cm = 0.5f; // 5 mm +static constexpr double diskRout_cm = 2.5f; // 25 mm +static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f}; + +// Petal walls specifications (cm) +static constexpr double kPetalZ_cm = 70.0f; // full wall height +static constexpr double kWallThick_cm = 0.015f; // 0.15 mm +static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical) +static constexpr double kOuterWallRadius_cm = 3.0f; // 30 mm (can be changed) +static constexpr double kEps_cm = 1.e-4f; + +// Coldplate specs (cm) +static constexpr double kColdplateRadius_cm = 2.6f; // 26 mm (outer radius) +static constexpr double kColdplateThickness_cm = 0.15f; // 1.5 mm +static constexpr double kColdplateZ_cm = 50.0f; // full length + +// ========== φ-span helpers (gap/arc → degrees) ========== +namespace +{ + +// Convert a linear gap at radius R into an angular gap (deg) +inline double degFromArc(double arc, double radius) +{ + // arc and radius in the SAME units (cm or mm); result in degrees + return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f; +} + +/** + * Compute silicon segment φ-span (degrees) inside one petal, + * when you know the number of petals and the linear gap at a given radius. + * + * All of: gap and radius must be in the SAME units (cm or mm). + * If you use cm everywhere (ROOT default), pass gap_cm and radius_cm. + */ +inline double phiSpanFromGap(int nPetals, double gap, double radius) +{ + if (nPetals <= 0 || radius <= 0.f) { + return 0.f; + } + const double petalPhiDeg = 360.f / nPetals; + const double phi = petalPhiDeg - degFromArc(gap, radius); + return phi > 0.f ? phi : 0.f; +} + +/** + * Compute silicon segment φ-span (degrees) from a known arc length at a given radius. + * arcLen and radius must be in the SAME units (cm or mm). + */ +inline double phiSpanFromArc(double arcLen, double radius) +{ + return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f; +} + +inline TGeoCombiTrans rotZ(double phiDeg) +{ + auto* r = new TGeoRotation(); + r->RotateZ(static_cast(phiDeg)); + return TGeoCombiTrans(0., 0., 0., r); +} +} // namespace + +// ============ Petal sub-builders (LOCAL coords only, no rotation) ========= + +// Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates. +static void addPetalWalls(TGeoVolume* petalAsm, + int nPetals, + double outerRadius_cm = kOuterWallRadius_cm, + bool withSideWalls = true, + bool fullCylindricalRadialWalls = false) +{ + if (!petalAsm) { + LOGP(error, "addPetalWalls: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + + if (!med) { + LOGP(warning, "Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created."); + return; + } + + const double halfZ = 0.5 * kPetalZ_cm; + + // In full-cylinder radial-wall mode we ignore nPetals for the radial walls. + const double halfPhi = fullCylindricalRadialWalls ? 180.0 : 0.5 * (360.0 / static_cast(nPetals)); + + // ---- Inner radial wall ---- + if (fullCylindricalRadialWalls) { + auto* s = new TGeoTube(static_cast(kInnerWallRadius_cm), + static_cast(kInnerWallRadius_cm + kWallThick_cm), + static_cast(halfZ)); + auto* v = new TGeoVolume("VD_InnerWallCyl", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } else { + auto* s = new TGeoTubeSeg(static_cast(kInnerWallRadius_cm), + static_cast(kInnerWallRadius_cm + kWallThick_cm), + static_cast(halfZ), + static_cast(-halfPhi), + static_cast(+halfPhi)); + auto* v = new TGeoVolume("VD_InnerWallArc", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } + + // ---- Outer radial wall ---- + if (fullCylindricalRadialWalls) { + auto* s = new TGeoTube(static_cast(outerRadius_cm), + static_cast(outerRadius_cm + kWallThick_cm), + static_cast(halfZ)); + auto* v = new TGeoVolume("VD_OuterWallCyl", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } else { + auto* s = new TGeoTubeSeg(static_cast(outerRadius_cm), + static_cast(outerRadius_cm + kWallThick_cm), + static_cast(halfZ), + static_cast(-halfPhi), + static_cast(+halfPhi)); + auto* v = new TGeoVolume("VD_OuterWallArc", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } + + // ---- Side plates (skip in "single petal full cylinders" mode) ---- + if (!withSideWalls) { + return; + } + + // ---- Side walls (boxes) at ±halfPhi ---- + const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm)); + auto* sideS = new TGeoBBox(static_cast(0.5f * radialLen), + static_cast(0.5f * kWallThick_cm), + static_cast(halfZ)); + auto* sideV = new TGeoVolume("VD_SideWall", sideS, med); + sideV->SetLineColor(kGray + 2); + sideV->SetTransparency(70); + + for (int sgn : {-1, +1}) { + const double phi = sgn * halfPhi; + const double rMid = kInnerWallRadius_cm + kWallThick_cm + 0.5f * radialLen; + const double rad = static_cast(TMath::DegToRad()); + const double x = rMid * std::cos(phi * rad); + const double y = rMid * std::sin(phi * rad); + auto* rot = new TGeoRotation(); + rot->RotateZ(static_cast(phi)); + auto* tr = new TGeoCombiTrans(static_cast(x), + static_cast(y), + 0.0, rot); + petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr); + } +} + +// Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical. +// φ-spans derive from spec gaps/arc; all local placement (no rotation). +static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0, bool fullCylinders) +{ + if (!petalAsm) { + LOGP(error, "addBarrelLayers: petalAsm is null"); + return; + } + + // Per spec (mm → cm) + constexpr double gapL0_cm = 0.163f; // 1.63 mm + constexpr double gapL1L2_cm = 0.12f; // 1.2 mm + constexpr double arcL0_cm = 0.6247f; // 6.247 mm + + // φ spans + const double phiL0_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); + const double phiL1_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); + const double phiL2_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); + + const std::string nameL0 = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "0"; + + if (!fullCylinders && rectangularL0) { + VDRectangularLayer L0(0, + nameL0, + kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm); + + // Correct translation: move to radius + half width along x + double x = kL0RectHeight_cm + L0.getChipThickness() / 2.; + LOGP(info, "Placing rectangular L0 at r={:.3f} cm (half-width={:.3f} cm)", x, 0.5f * kL0RectWidth_cm); + double y = 0.0; + double z = 0.0; + + // Correct rotation: rotate 90 degrees around z so long side is horizontal + auto* rot = new TGeoRotation(); + rot->RotateZ(90.0); + + auto* tr = new TGeoCombiTrans(x, y, z, rot); + L0.createLayer(petalAsm, tr); + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Plane, /*idx*/ 0); + } else { + VDCylindricalLayer L0(0, + nameL0, + kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm); + L0.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 0); + } + + const std::string nameL1 = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "1"; + + VDCylindricalLayer L1(1, + nameL1, + kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm); + L1.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 1); + + const std::string nameL2 = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "2"; + + VDCylindricalLayer L2(2, + nameL2, + kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm); + L2.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 2); +} + +// Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. +static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId, bool fullCylinders = false) +{ + if (!petalAsm) { + LOGP(error, "addColdPlate: petalAsm is null"); + return; + } + + // Resolve medium: prefer provided medium, otherwise try to fetch from geo manager + const TGeoMedium* med = gGeoManager->GetMedium("ALICE3_TRKSERVICES_CERAMIC"); + if (!med) { + LOGP(error, "addColdPlate: can't find the medium."); + } + + // Angular span for one petal (deg) + constexpr double gapL1L2_cm = 0.12f; // 1.2 mm + + // φ spans + const double phiSpanColdplate_deg = + fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined in normal mode + const double halfPhiDeg = 0.5 * phiSpanColdplate_deg; + const double startPhi = -halfPhiDeg; + const double endPhi = +halfPhiDeg; + + // Build tube segment: inner radius, outer radius = inner + thickness, half-length Z + auto* shape = new TGeoTubeSeg(static_cast(kColdplateRadius_cm), + static_cast(kColdplateRadius_cm + kColdplateThickness_cm), + static_cast(0.5 * kColdplateZ_cm), + static_cast(startPhi), + static_cast(endPhi)); + + TString volName = TString::Format("Petal%d_Coldplate", petalId); + auto* coldVol = new TGeoVolume(volName, shape, med); + coldVol->SetLineColor(kAzure - 3); + coldVol->SetTransparency(10); + + // Place in local petal coordinates (no extra transform); keep object alive by allocating shape/volume on heap. + petalAsm->AddNode(coldVol, 1); + + LOGP(info, "Adding cold plate {} r={:.3f} cm t={:.3f} cm Lz={:.3f} cm φ=[{:.3f}, {:.3f}]", + volName.Data(), kColdplateRadius_cm, kColdplateThickness_cm, kColdplateZ_cm, startPhi, endPhi); +} + +// Add IRIS service module(s) as aluminum annular cylinders placed outside the petals. +// The two modules are placed at z = ±(36 + halfLength). +static void addIRISServiceModules(TGeoVolume* petalAsm, int nPetals) +{ + if (!petalAsm) { + LOGP(error, "addIRISServiceModules: petalAsm is null"); + return; + } + + auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70); + const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl); + + if (!med) { + LOGP(error, "addIRISServiceModules: ALUMINUM medium not found."); + return; + } + + constexpr double radius = 3.2; // cm (inner radius) + constexpr double thickness = 0.133; // cm (radial thickness) + constexpr double halfLength = 19.5; // cm (half-length along Z) + const double rIn = radius; + const double rOut = radius + thickness; + + // Petal angular span. If you have an exact half-φ from your walls, use it here. + const double halfPhi_deg = 0.5 * (360.0 / double(nPetals)); + + // Create shape once and reuse + auto* segSh = new TGeoTubeSeg( + "IRIS_SERVICE_SEGsh", + rIn, rOut, + halfLength, + -halfPhi_deg, halfPhi_deg); + + // Positive Z module + TString namePos = "IRIS_Service_Pos"; + auto* volPos = new TGeoVolume(namePos, segSh, med); + volPos->SetLineColor(kRed + 2); + volPos->SetTransparency(50); + + // Negative Z module: reuse same shape object, give different name + TString nameNeg = "IRIS_Service_Neg"; + auto* volNeg = new TGeoVolume(nameNeg, segSh, med); + volNeg->SetLineColor(kRed + 2); + volNeg->SetTransparency(50); + + // Translations (heap-allocated so ROOT keeps them) + const double zpos = 36.0 + halfLength; + auto* transPos = new TGeoTranslation(0.0, 0.0, static_cast(zpos)); + auto* transNeg = new TGeoTranslation(0.0, 0.0, static_cast(-zpos)); + + // Add to mother volume + petalAsm->AddNode(volPos, 1, transPos); + petalAsm->AddNode(volNeg, 2, transNeg); + + LOGP(info, "Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut); +} + +// Only the A-side "inside vacuum" piece participates in the cutout. +static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) +{ + if (!petalAsm) { + LOGP(error, "addIRISServiceModulesSegmented: petalAsm is null"); + return; + } + + // --- Service geometry (same as your previous values) + constexpr double rIn = 3.2; // cm + constexpr double thickness = 0.133; // cm + constexpr double rOut = rIn + thickness; + constexpr double halfLen = 19.5; // cm + constexpr double z0 = 36.0 + halfLen; // 55.5 cm center of +Z service + const double zMinA = z0 - halfLen; // 36.0 cm + const double zMaxA = z0 + halfLen; // 75.0 cm + + // --- Vacuum vessel window around z∈[-L/2, +L/2] with wall thickness on +Z side + // Keep these in sync with TRKServices::createVacuumCompositeShape() + constexpr double vacuumVesselLength = 76.0; // cm + constexpr double vacuumVesselThickness = 0.08; // cm (0.8 mm) + const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm + const double gapStart = halfVess; // 38.00 + const double gapEnd = halfVess + vacuumVesselThickness; // 38.08 + + // --- Petal φ-span (segment) + const double halfPhi = 0.5 * (360.0 / double(nPetals)); + + auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70); + const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl); + + if (!med) { + LOGP(error, "addIRISServiceModules: ALUMINUM medium not found."); + return; + } + + // ========================= + // C-side (negative Z) whole + // ========================= + { + auto* sh = new TGeoTubeSeg(rIn, rOut, halfLen, -halfPhi, +halfPhi); + auto* vN = new TGeoVolume("IRIS_Service_Neg", sh, med); + vN->SetLineColor(kRed + 2); + vN->SetTransparency(55); + petalAsm->AddNode(vN, 1, new TGeoTranslation(0., 0., -(z0))); + } + + // ===================================== + // A-side (positive Z): split with a gap + // ===================================== + // Piece 1 (INSIDE vacuum): z ∈ [zMinA, min(zMaxA, gapStart)] → goes into cutout + const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA); // expected ~2.0 cm + if (L_inVac > 0) { + const double dz = 0.5 * L_inVac; + const double zc = zMinA + dz; // center of lower slice, ≈ 37.0 cm + auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, halfPhi); + sh->SetName("IRIS_SERVICE_POS_INVACsh"); + auto* vP = new TGeoVolume("IRIS_Service_Pos_InVac", sh, med); + vP->SetLineColor(kRed + 2); + vP->SetTransparency(55); + petalAsm->AddNode(vP, 1, new TGeoTranslation(0., 0., zc)); + LOGP(info, "IRIS A-side (InVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm", + zc - dz, zc + dz, 2 * dz); + } else { + LOGP(warning, "IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)"); + } + + // Gap (no material): (gapStart, gapEnd) = (38.00, 38.08) + + // Piece 2 (OUT of vacuum): z ∈ [max(zMinA, gapEnd), zMaxA] → NOT in cutout + const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd)); // expected ~36.92 cm + if (L_outVac > 0) { + const double dz = 0.5 * L_outVac; + const double zc = std::max(zMinA, gapEnd) + dz; // center of upper slice + auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, +halfPhi); + sh->SetName("IRIS_SERVICE_POS_OUTVACsh"); + auto* vP = new TGeoVolume("IRIS_Service_Pos_OutVac", sh, med); + vP->SetLineColor(kRed + 1); + vP->SetTransparency(70); + petalAsm->AddNode(vP, 2, new TGeoTranslation(0., 0., +zc)); + LOGP(info, "IRIS A-side (OutVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm", + zc - dz, zc + dz, 2 * dz); + } else { + LOGP(warning, "IRIS A-side (OutVac): no upper piece (L_outVac<=0)"); + } +} + +// Build disks in local coords: each disk gets only a local Z translation. +// φ span from gap at rOut. +static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID, bool fullCylinders) +{ + + if (!petalAsm) { + LOGP(error, "addDisks: petalAsm is null"); + return; + } + + const double phiDisk_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + + for (int i = 0; i < 6; ++i) { + const std::string nameD = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalDiskPattern()) + std::to_string(i); + + VDDiskLayer disk(i, + nameD, + kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[i]); + + // Local Z placement only + auto* tr = new TGeoTranslation(0.0, 0.0, static_cast(disk.getZPosition())); + disk.createLayer(petalAsm, tr); + registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Region::Disk, VDSensorDesc::Type::Plane, /*idx*/ i); + } +} + +// Add Z end-cap walls to "close" the petal/cylinder volume at zMin and zMax. +// Implemented as thin rings (TGeoTube) with thickness 'capThick_cm' in Z, +// spanning radii [rIn_cm, rOut_cm]. +static void addPetalEndCaps(TGeoVolume* petalAsm, + int petalId, + double rIn_cm, + double rOut_cm, + double zMin_cm, + double zMax_cm, + double capThick_cm) +{ + if (!petalAsm) { + LOGP(error, "addPetalEndCaps: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = + matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + + if (!med) { + LOGP(warning, + "addPetalEndCaps: ALICE3_TRKSERVICES_ALUMINIUM5083 not found, caps not created."); + return; + } + + const double halfT = 0.5 * capThick_cm; + + auto* sh = new TGeoTube(static_cast(rIn_cm), + static_cast(rOut_cm), + static_cast(halfT)); + + TString vname = Form("Petal%d_ZCap", petalId); + auto* v = new TGeoVolume(vname, sh, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + + auto* trMin = new TGeoTranslation(0.0, 0.0, + static_cast(zMin_cm + halfT)); + auto* trMax = new TGeoTranslation(0.0, 0.0, + static_cast(zMax_cm - halfT)); + + petalAsm->AddNode(v, 1, trMin); + petalAsm->AddNode(v, 2, trMax); +} + +// Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords. +static TGeoVolume* buildPetalAssembly(int nPetals, + int petalID, + bool rectangularL0, + bool fullCylinders, + bool withSideWalls) +{ + auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); + + // In the special mode: no side walls, but keep radial walls as FULL cylinders. + addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm, + /*withSideWalls=*/withSideWalls, + /*fullCylindricalRadialWalls=*/fullCylinders); + + addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders); + // addDisks(petalAsm, nPetals, petalID, fullCylinders); // disks removed according to the v3b layout + + addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false); + addIRISServiceModulesSegmented(petalAsm, nPetals); + + return petalAsm; +} + +static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks) +{ + // IMPORTANT: keep naming consistent with createIRIS4/5 (PETAL_%d) + auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); + + // Radial walls only: full 360° cylinders, no side plates + addPetalWalls(petalAsm, + /*nPetals=*/1, + /*outerRadius_cm=*/kOuterWallRadius_cm, + /*withSideWalls=*/false, + /*fullCylindricalRadialWalls=*/true); + + // --- Z end-cap walls to close the petal in Z --- + { + const double zMin = -0.5 * kLenZ_cm; + const double zMax = +0.5 * kLenZ_cm; + const double rIn = kInnerWallRadius_cm; + const double rOut = kOuterWallRadius_cm + kWallThick_cm; + + addPetalEndCaps(petalAsm, + petalID, + rIn, + rOut, + zMin, + zMax, + kWallThick_cm); + } + + // Full 360° barrel cylinders + addBarrelLayers(petalAsm, + /*nPetals=*/1, + /*petalID=*/petalID, + /*rectangularL0=*/false, + /*fullCylinders=*/true); + + addColdPlate(petalAsm, 1, petalID, /*fullCylinders=*/true); + addIRISServiceModulesSegmented(petalAsm, /*nPetals=*/1); + + // Optionally add full 360° disks + if (withDisks) { + addDisks(petalAsm, + /*nPetals=*/1, + /*petalID=*/petalID, + /*fullCylinders=*/true); + } + + return petalAsm; +} + +// =================== Public entry points =================== + +void createIRIS4Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4Geometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false, + /*fullCylinders=*/false, + /*withSideWalls=*/true); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRIS5Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS5Geometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true, + /*fullCylinders=*/false, + /*withSideWalls=*/true); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRIS4aGeometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4aGeometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 3; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false, + /*fullCylinders=*/false, + /*withSideWalls=*/true); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRISGeometryFullCyl(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometryFullCyl: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/false); + motherVolume->AddNode(petal, 1, nullptr); + + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometryFullCylDisks: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/true); + motherVolume->AddNode(petal, 1, nullptr); + + // Same cutout pipeline as createIRIS4/5: + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0) +{ + auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0, false, true); + + // Optionally rotate the petal for display + const double phiDeg = (360.f / static_cast(nPetals)) * (static_cast(petalID) + 0.5f); + auto* R = new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", phiDeg, 0, 0)); + motherVolume->AddNode(petal, 1, R); + + LOGP(info, "Debug: Added Petal{} to {}", petalID, motherVolume->GetName()); +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx new file mode 100644 index 0000000000000..411dd485684b9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -0,0 +1,554 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDLayer.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" + +#include "Framework/Logger.h" + +#include "TGeoTube.h" +#include "TGeoBBox.h" +#include "TGeoVolume.h" +#include "TGeoMatrix.h" +#include "TGeoManager.h" + +#include "TMath.h" + +namespace o2 +{ +namespace trk +{ +// Base layer constructor +VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0) + : mLayerNumber(layerNumber), mLayerName(layerName), mX2X0(layerX2X0), mModuleWidth(4.54) +{ + constexpr double kSiX0_cm = 9.5; // Radiation length of Silicon in cm + mChipThickness = mX2X0 * kSiX0_cm; + + mSensorThickness = o2::trk::constants::VD::silicon::thickness; // cm +} + +// VDCylindricalLayer constructor +VDCylindricalLayer::VDCylindricalLayer(int layerNumber, const std::string& layerName, double layerX2X0, double radius, + double phiSpanDeg, double lengthZ, double lengthSensZ) + : VDLayer(layerNumber, layerName, layerX2X0), mRadius(radius), mPhiSpanDeg(phiSpanDeg), mLengthZ(lengthZ), mLengthSensZ(lengthSensZ) +{ + LOGP(info, "Creating VD cylindrical layer: id: {} name: {} x2X0: {} radius: {} phiSpanDeg: {} lengthZ: {} lengthSensZ: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, radius, phiSpanDeg, lengthZ, lengthSensZ, mChipThickness); +} + +// VDRectangularLayer constructor +VDRectangularLayer::VDRectangularLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double width, double lengthZ, double lengthSensZ) + : VDLayer(layerNumber, layerName, layerX2X0), mWidth(width), mLengthZ(lengthZ), mLengthSensZ(lengthSensZ) +{ + + if (mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(fatal, "Invalid sensor length: sensZ={} layerZ={}", mLengthSensZ, mLengthZ); + } + LOGP(info, "Creating VD rectangular layer: id: {} name: {} x2X0: {} width: {} lengthZ: {} lengthSensZ: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, width, lengthZ, lengthSensZ, mChipThickness); +} + +// VDDiskLayer constructor +VDDiskLayer::VDDiskLayer(int layerNumber, const std::string& layerName, double layerX2X0, double rMin, double rMax, + double phiSpanDeg, double zPos) + : VDLayer(layerNumber, layerName, layerX2X0), mRMin(rMin), mRMax(rMax), mPhiSpanDeg(phiSpanDeg), mZPos(zPos) +{ + + LOGP(info, "Creating VD disk layer: id: {} name: {} x2X0: {} rMin: {} rMax: {} phiSpanDeg: {} zPos: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, rMin, rMax, phiSpanDeg, zPos, mChipThickness); +} + +/* +** Create sensor +*/ + +TGeoVolume* VDCylindricalLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double rIn = mRadius; + const double rOut = mRadius + mSensorThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); + vol->SetLineColor(kYellow); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDRectangularLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mSensorThickness; + const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer + + auto* shape = new TGeoBBox(hx, hy, hz); + auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); + vol->SetLineColor(kYellow); + vol->SetTransparency(30); + + return vol; +} + +TGeoVolume* VDDiskLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk sensor dims: rMin={}, rMax={}, t={}, phiSpanDeg={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double halfThickness = 0.5 * mSensorThickness; // active sensor thickness along Z + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + + // Same geometry as the layer (identical radii + phi span + thickness) + auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + + auto* sensVol = new TGeoVolume(sensName.c_str(), shape, medSi); + sensVol->SetLineColor(kYellow); + sensVol->SetTransparency(30); + + return sensVol; +} + +/* +** Create metal stack +*/ + +TGeoVolume* VDCylindricalLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; // nothing to add + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double rIn = mRadius + mSensorThickness; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDRectangularLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double hx = 0.5 * mWidth; + const double hy = 0.5 * metalT; + const double hz = 0.5 * mLengthSensZ; + + auto* shape = new TGeoBBox(hx, hy, hz); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDDiskLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; + } + + if (mRMin < 0 || mRMax <= mRMin || mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk metal dims: rMin={}, rMax={}, metalT={}, phiSpanDeg={}", + mRMin, mRMax, metalT, mPhiSpanDeg); + return nullptr; + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double halfThickness = 0.5 * metalT; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +/* +** Create chip +*/ + +TGeoVolume* VDCylindricalLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* chipShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + + // sensor + if (auto* sensVol = createSensor()) { + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + } + + // metal stack + if (auto* metalVol = createMetalStack()) { + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, nullptr); // concentric, no translation needed + } + + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + return chipVol; +} + +TGeoVolume* VDRectangularLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; + const double hz = 0.5 * mLengthSensZ; + + auto* chipShape = new TGeoBBox(hx, hy, hz); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + + // sensor (place it on the "bottom" side, like TRK) + if (auto* sensVol = createSensor()) { + auto* transSens = new TGeoTranslation(0.0, -(mChipThickness - mSensorThickness) / 2, 0.0); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + } + + // metal stack (remaining thickness on top) + if (auto* metalVol = createMetalStack()) { + auto* transMetal = new TGeoTranslation(0.0, +mSensorThickness / 2, 0.0); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, transMetal); + } + + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + return chipVol; +} + +TGeoVolume* VDDiskLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk chip dims: rMin={}, rMax={}, t={}, phi={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double halfThickness = 0.5 * mChipThickness; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + + // Sensor slab (sensitive) placed on one side in Z (TRK-like stacking convention) + if (auto* sensVol = createSensor()) { + const double zSens = -(mChipThickness - mSensorThickness) / 2.0; + auto* tSens = new TGeoTranslation(0.0, 0.0, zSens); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, tSens); + } + + // Metal stack slab (non-sensitive), remaining thickness, also silicon + if (auto* metalVol = createMetalStack()) { + const double zMetal = +mSensorThickness / 2.0; + auto* tMetal = new TGeoTranslation(0.0, 0.0, zMetal); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, tMetal); + } + + return chipVol; +} + +/* +** Create layer +*/ + +// Cylindrical layer +void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + // Sanity + if (mRadius <= 0 || mChipThickness <= 0 || mLengthZ <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0 || + mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(error, "Invalid cylindrical dimensions: r={}, t={}, Z={}, phi={}, sensZ={}", + mRadius, mChipThickness, mLengthZ, mPhiSpanDeg, mLengthSensZ); + return; + } + + // AIR container (layer) + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthZ; + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + + auto* layerShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Chip volume (must use mLengthSensZ internally) + TGeoVolume* chipVol = VDCylindricalLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDCylindricalLayer::createChip() returned null"); + return; + } + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + // Tiling: edge-to-edge if sensor shorter than layer; else single centered + // const auto zCenters = (mLengthSensZ < mLengthZ) + // ? centersNoGapZ(mLengthZ, mLengthSensZ) + // : std::vector{0.0}; + // + // int copyNo = 1; + // for (double zc : zCenters) { + // TGeoTranslation tz(0.0, 0.0, zc); + // layerVol->AddNode(sensorVol, copyNo++, (zc == 0.0 && zCenters.size() == 1) ? nullptr : &tz); + // } + + motherVolume->AddNode(layerVol, 1, combiTrans); +} + +// Rectangular layer +void VDRectangularLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + if (mWidth <= 0 || mChipThickness <= 0 || mLengthZ <= 0 || + mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(error, "Invalid rectangular dims: W={}, t={}, Z={}, sensZ={}", + mWidth, mChipThickness, mLengthZ, mLengthSensZ); + return; + } + + // AIR container (layer) + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; + const double hz = 0.5 * mLengthZ; + + auto* layerShape = new TGeoBBox(hx, hy, hz); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor volume (uses mLengthSensZ internally) + TGeoVolume* chipVol = VDRectangularLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDRectangularLayer::chipVol() returned null"); + return; + } + + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + // Tiling along Z, edge - to - edge if needed + // const auto zCenters = (mLengthSensZ < mLengthZ) + // ? centersNoGapZ(mLengthZ, mLengthSensZ) + // : std::vector{0.0}; + // + // int copyNo = 1; + // for (double zc : zCenters) { + // TGeoTranslation tz(0.0, 0.0, zc); + // layerVol->AddNode(sensorVol, copyNo++, (zc == 0.0 && zCenters.size() == 1) ? nullptr : &tz); + // } + + motherVolume->AddNode(layerVol, 1, combiTrans); +} + +// Disk layer +void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk dims: rMin={}, rMax={}, t={}, phi={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return; + } + + // For disks the thickness is along Z and equals mChipThickness + const double halfThickness = 0.5 * mChipThickness; + const double halfPhi = 0.5 * mPhiSpanDeg; + + // AIR container (layer) + auto* layerShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor (same size & shape as the layer for disks) + TGeoVolume* chipVol = VDDiskLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDDiskLayer::createChip() returned null"); + return; + } + + // Insert single sensor (no Z-segmentation for disks) + layerVol->AddNode(chipVol, 1, nullptr); + + TGeoTranslation tz(0.0, 0.0, mZPos); + motherVolume->AddNode(layerVol, 1, combiTrans ? combiTrans : &tz); +} + +// ClassImp(VDLayer); +// ClassImp(VDCylindricalLayer); +// ClassImp(VDRectangularLayer); +// ClassImp(VDDiskLayer); + +} // namespace trk +} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index e86ed7982c85b..d6c8ea85c2bbd 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -14,17 +14,23 @@ o2_add_library(TRKWorkflow SOURCES src/DigitReaderSpec.cxx src/DigitWriterSpec.cxx src/TrackerSpec.cxx + src/TrackWriterSpec.cxx src/RecoWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::GPUWorkflow O2::SimConfig O2::DataFormatsITSMFT O2::SimulationDataFormat - O2::DPLUtils) + O2::DPLUtils + O2::TRKBase + O2::TRKSimulation + O2::TRKReconstruction + nlohmann_json::nlohmann_json) o2_add_executable(reco-workflow SOURCES src/trk-reco-workflow.cxx COMPONENT_NAME alice3-trk PUBLIC_LINK_LIBRARIES O2::TRKWorkflow O2::TRKSimulation - O2::ITStracking) \ No newline at end of file + O2::TRKReconstruction + O2::ITStracking) diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md new file mode 100644 index 0000000000000..afb30ed6dbdd3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md @@ -0,0 +1,130 @@ +# TRK Reconstruction Workflow + +This document describes how to run the TRK (ALICE 3 Tracker) reconstruction workflow and provides examples of configuration files. + +## Overview + +The TRK reconstruction workflow performs track reconstruction from simulated hits, producing reconstructed tracks with MC truth labels. The workflow currently supports the track reconstruction from hits using the Cellular Automaton (CA) algorithm. The ouput is stored to a ROOT file for offline analysis (example of QA macro provided in `macros/test/CheckTracksCA.C`). + +## Quick Start + +### Basic Command + +```bash +o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +### Command Line Options + +- `--tracking-from-hits-config `: Path to tracking configuration JSON file (required) +- `-b`: Batch mode (no GUI) +- `--disable-root-output`: Skip writing tracks to ROOT file +- `--help`: Show all available options + +## Configuration File + +The tracking configuration is provided via a JSON file that specifies: +1. Input file paths +2. Geometry parameters (magnetic field, detector pitch) +3. Tracking algorithm parameters (can specify multiple iterations) + +### Example Configuration (`config_tracker.json`) + +```json +{ + "inputfiles": { + "hits": "o2sim_HitsTRK.root", + "geometry": "o2sim_geometry.root", + "mcHeader": "o2sim_MCHeader.root", + "kinematics": "o2sim_Kine.root" + }, + "geometry": { + "bz": 5.0, + "pitch": [0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004] + }, + "trackingparams": [{ + "NLayers": 11, + "DeltaROF": 0, + "LayerZ": [25.1, 25.1, 25.1, 64.2, 64.2, 64.2, 64.2, 64.2, 128.5, 128.5, 128.5], + "LayerRadii": [0.5, 1.2, 2.5, 7.05, 9.05, 12.05, 20.05, 30.05, 45.05, 60.5, 80.05], + "LayerxX0": [0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01], + "LayerResolution": [0.0003, 0.0003, 0.0003, 0.0003, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012], + "SystErrorY2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "SystErrorZ2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "ZBins": 256, + "PhiBins": 128, + "nROFsPerIterations": -1, + "UseDiamond": false, + "Diamond": [0.0, 0.0, 0.0], + "AllowSharingFirstCluster": false, + "ClusterSharing": 0, + "MinTrackLength": 7, + "NSigmaCut": 10, + "PVres": 0.01, + "TrackletMinPt": 0.1, + "TrackletsPerClusterLimit": 2.0, + "CellDeltaTanLambdaSigma": 0.007, + "CellsPerClusterLimit": 2.0, + "MaxChi2ClusterAttachment": 60.0, + "MaxChi2NDF": 30.0, + "ReseedIfShorter": 6, + "MinPt": [0.0, 0.0, 0.0, 0.0], + "StartLayerMask": 4095, + "RepeatRefitOut": false, + "ShiftRefToCluster": true, + "FindShortTracks": false, + "PerPrimaryVertexProcessing": false, + "SaveTimeBenchmarks": false, + "DoUPCIteration": false, + "FataliseUponFailure": true, + "UseTrackFollower": true, + "UseTrackFollowerTop": false, + "UseTrackFollowerBot": false, + "UseTrackFollowerMix": true, + "TrackFollowerNSigmaCutZ": 1.0, + "TrackFollowerNSigmaCutPhi": 1.0, + "createArtefactLabels": false, + "PrintMemory": false, + "DropTFUponFailure": false + }] +} +``` +Note that the `trackingparams` field can contain multiple sets of parameters for different iterations of the tracking algorithm. The example above shows a single iteration with 11 layers and it is **not** optimized. + +## Complete Workflow Example + +### 1. Run Simulation + +First, generate simulation data: + +```bash +o2-sim-serial-run5 -n 200 -g pythia8hi -m TRK --configKeyValues "Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.width[2]=5;TRKBase.layoutML=kTurboStaves;TRKBase.layoutOL=kStaggered;" +``` + +This produces, among other files: +- `o2sim_HitsTRK.root` +- `o2sim_geometry.root` +- `o2sim_MCHeader.root` +- `o2sim_Kine.root` +That will be used by the reconstruction as currently we do not have clusters. + +### 2. Run Reconstruction + +Execute the tracking workflow: + +```bash +o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +This produces: +- `o2trac_trk.root`: Reconstructed tracks with MC labels + +### 3. Run Quality Assurance + +Analyze the tracking performance: + +```bash +root -l +.L CheckTracksCA.C+ +CheckTracksCA("o2trac_trk.root", "o2sim_Kine.root", "o2sim_HitsTRK.root", "trk_qa_output.root") +``` diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h index 0c2489aa4b9c4..7046955a20c2e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -13,7 +13,8 @@ #define O2_TRK_RECOWORKFLOW_H #include "Framework/WorkflowSpec.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" +#include namespace o2::trk { @@ -21,11 +22,12 @@ namespace reco_workflow { o2::framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, bool useGPUWF = false, - o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU); + o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); } } // namespace o2::trk diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h similarity index 71% rename from Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h rename to Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h index 51dc5a6481eb5..105504e7c9fe6 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h @@ -9,23 +9,23 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// @file ClusterWriterSpec.h +/// @file TrackWriterSpec.h -#ifndef O2_MFT_CLUSTERWRITER_H_ -#define O2_MFT_CLUSTERWRITER_H_ +#ifndef O2_TRK_TRACKWRITER +#define O2_TRK_TRACKWRITER #include "Framework/DataProcessorSpec.h" namespace o2 { -namespace mft +namespace trk { /// create a processor spec -/// write MFT clusters a root file -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); +/// write TRK tracks to ROOT file +o2::framework::DataProcessorSpec getTrackWriterSpec(bool useMC); -} // namespace mft +} // namespace trk } // namespace o2 -#endif /* O2_MFT_CLUSTERWRITER_H */ +#endif /* O2_TRK_TRACKWRITER */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h index 3c82a4fd7b89d..33b25737bbc29 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -19,13 +19,18 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include + +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/TrackingInterface.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TStopwatch.h" +#include + namespace o2::trk { class TrackerDPL : public framework::Task @@ -33,7 +38,8 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, - gpu::GPUDataTypes::DeviceType dType = gpu::GPUDataTypes::DeviceType::CPU); + const std::string& hitRecoConfig, + gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; @@ -43,14 +49,18 @@ class TrackerDPL : public framework::Task private: void updateTimeDependentParams(framework::ProcessingContext& pc); + std::vector createTrackingParamsFromConfig(); // std::unique_ptr mRecChain = nullptr; // std::unique_ptr mChainITS = nullptr; // std::shared_ptr mGGCCDBRequest; // ITSTrackingInterface mITSTrackingInterface; + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; + nlohmann::json mHitRecoConfig; TStopwatch mTimer; }; -framework::DataProcessorSpec getTrackerSpec(bool useMC, gpu::GPUDataTypes::DeviceType dType = gpu::GPUDataTypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::trk -#endif /* O2_TRK_TRACKERDPL */ \ No newline at end of file +#endif /* O2_TRK_TRACKERDPL */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index 3b2b44729b259..5f6cbe2f96b04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -11,21 +11,30 @@ #include "TRKWorkflow/RecoWorkflow.h" #include "TRKWorkflow/TrackerSpec.h" +#include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" +#include + namespace o2::trk::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits, bool upstreamClusters, bool disableRootOutput, bool useGPUWF, - o2::gpu::GPUDataTypes::DeviceType dtype) + o2::gpu::gpudatatypes::DeviceType dtype) { framework::WorkflowSpec specs; - specs.emplace_back(o2::trk::getTrackerSpec(useMC, dtype)); + specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + + if (!disableRootOutput) { + specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + } + return specs; } -} // namespace o2::trk::reco_workflow \ No newline at end of file +} // namespace o2::trk::reco_workflow diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx new file mode 100644 index 0000000000000..1606c32a0ea78 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx @@ -0,0 +1,57 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TrackWriterSpec.cxx + +#include + +#include "TRKWorkflow/TrackWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace trk +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using LabelsType = std::vector; +using namespace o2::header; + +DataProcessorSpec getTrackWriterSpec(bool useMC) +{ + // Spectators for logging + auto tracksSize = std::make_shared(0); + auto tracksSizeGetter = [tracksSize](std::vector const& tracks) { + *tracksSize = tracks.size(); + }; + auto logger = [tracksSize]() { + LOG(info) << "TRKTrackWriter pulled " << *tracksSize << " tracks"; + }; + + return MakeRootTreeWriterSpec("trk-track-writer", + "o2trac_trk.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with TRK tracks"}, + BranchDefinition>{InputSpec{"tracks", "TRK", "TRACKS", 0}, + "TRKTrack", + tracksSizeGetter}, + BranchDefinition{InputSpec{"labels", "TRK", "TRACKSMCTR", 0}, + "TRKTrackMCTruth", + (useMC ? 1 : 0), // one branch if mc labels enabled + ""})(); +} + +} // namespace trk +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 4057bab3b948f..8fc67f0fa5567 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -10,11 +10,27 @@ // or submit itself to any jurisdiction. #include +#include +#include "DetectorsBase/GeometryManager.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Configuration.h" +#include "Field/MagneticField.h" +#include "Field/MagFieldParam.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKSimulation/Hit.h" +#include "TRKReconstruction/TimeFrame.h" #include "TRKWorkflow/TrackerSpec.h" +#include + +#include +#include namespace o2 { @@ -25,8 +41,14 @@ using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, - o2::gpu::GPUDataTypes::DeviceType dType) + const std::string& hitRecoConfigFileName, + o2::gpu::gpudatatypes::DeviceType dType) { + if (!hitRecoConfigFileName.empty()) { + std::ifstream configFile(hitRecoConfigFileName); + mHitRecoConfig = nlohmann::json::parse(configFile); + } + // mITSTrackingInterface.setTrackingMode(trMode); } @@ -46,13 +68,291 @@ void TrackerDPL::stop() LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +std::vector TrackerDPL::createTrackingParamsFromConfig() +{ + std::vector trackingParams; + + if (!mHitRecoConfig.contains("trackingparams") || !mHitRecoConfig["trackingparams"].is_array()) { + LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector."); + return trackingParams; + } + + for (const auto& paramConfig : mHitRecoConfig["trackingparams"]) { + o2::its::TrackingParameters params; + + // Parse integer parameters + if (paramConfig.contains("NLayers")) { + params.NLayers = paramConfig["NLayers"].get(); + } + if (paramConfig.contains("DeltaROF")) { + params.DeltaROF = paramConfig["DeltaROF"].get(); + } + if (paramConfig.contains("ZBins")) { + params.ZBins = paramConfig["ZBins"].get(); + } + if (paramConfig.contains("PhiBins")) { + params.PhiBins = paramConfig["PhiBins"].get(); + } + if (paramConfig.contains("nROFsPerIterations")) { + params.nROFsPerIterations = paramConfig["nROFsPerIterations"].get(); + } + if (paramConfig.contains("ClusterSharing")) { + params.ClusterSharing = paramConfig["ClusterSharing"].get(); + } + if (paramConfig.contains("MinTrackLength")) { + params.MinTrackLength = paramConfig["MinTrackLength"].get(); + } + if (paramConfig.contains("ReseedIfShorter")) { + params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get(); + } + if (paramConfig.contains("StartLayerMask")) { + params.StartLayerMask = paramConfig["StartLayerMask"].get(); + } + + // Parse float parameters + if (paramConfig.contains("NSigmaCut")) { + params.NSigmaCut = paramConfig["NSigmaCut"].get(); + } + if (paramConfig.contains("PVres")) { + params.PVres = paramConfig["PVres"].get(); + } + if (paramConfig.contains("TrackletMinPt")) { + params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); + } + if (paramConfig.contains("TrackletsPerClusterLimit")) { + params.TrackletsPerClusterLimit = paramConfig["TrackletsPerClusterLimit"].get(); + } + if (paramConfig.contains("CellDeltaTanLambdaSigma")) { + params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); + } + if (paramConfig.contains("CellsPerClusterLimit")) { + params.CellsPerClusterLimit = paramConfig["CellsPerClusterLimit"].get(); + } + if (paramConfig.contains("MaxChi2ClusterAttachment")) { + params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); + } + if (paramConfig.contains("MaxChi2NDF")) { + params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); + } + if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { + params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); + } + if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { + params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); + } + + // Parse boolean parameters + if (paramConfig.contains("UseDiamond")) { + params.UseDiamond = paramConfig["UseDiamond"].get(); + } + if (paramConfig.contains("AllowSharingFirstCluster")) { + params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get(); + } + if (paramConfig.contains("RepeatRefitOut")) { + params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get(); + } + if (paramConfig.contains("ShiftRefToCluster")) { + params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); + } + if (paramConfig.contains("FindShortTracks")) { + params.FindShortTracks = paramConfig["FindShortTracks"].get(); + } + if (paramConfig.contains("PerPrimaryVertexProcessing")) { + params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); + } + if (paramConfig.contains("SaveTimeBenchmarks")) { + params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get(); + } + if (paramConfig.contains("DoUPCIteration")) { + params.DoUPCIteration = paramConfig["DoUPCIteration"].get(); + } + if (paramConfig.contains("FataliseUponFailure")) { + params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); + } + if (paramConfig.contains("UseTrackFollower")) { + params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); + } + if (paramConfig.contains("UseTrackFollowerTop")) { + params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); + } + if (paramConfig.contains("UseTrackFollowerBot")) { + params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); + } + if (paramConfig.contains("UseTrackFollowerMix")) { + params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); + } + if (paramConfig.contains("createArtefactLabels")) { + params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); + } + if (paramConfig.contains("PrintMemory")) { + params.PrintMemory = paramConfig["PrintMemory"].get(); + } + if (paramConfig.contains("DropTFUponFailure")) { + params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get(); + } + + // Parse vector parameters + if (paramConfig.contains("LayerZ")) { + params.LayerZ = paramConfig["LayerZ"].get>(); + } + if (paramConfig.contains("LayerRadii")) { + params.LayerRadii = paramConfig["LayerRadii"].get>(); + } + if (paramConfig.contains("LayerxX0")) { + params.LayerxX0 = paramConfig["LayerxX0"].get>(); + } + if (paramConfig.contains("LayerResolution")) { + params.LayerResolution = paramConfig["LayerResolution"].get>(); + } + if (paramConfig.contains("SystErrorY2")) { + params.SystErrorY2 = paramConfig["SystErrorY2"].get>(); + } + if (paramConfig.contains("SystErrorZ2")) { + params.SystErrorZ2 = paramConfig["SystErrorZ2"].get>(); + } + if (paramConfig.contains("MinPt")) { + params.MinPt = paramConfig["MinPt"].get>(); + } + + // Parse Diamond array + if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) { + params.Diamond[0] = paramConfig["Diamond"][0].get(); + params.Diamond[1] = paramConfig["Diamond"][1].get(); + params.Diamond[2] = paramConfig["Diamond"][2].get(); + } + + // Parse size_t parameter + if (paramConfig.contains("MaxMemory")) { + params.MaxMemory = paramConfig["MaxMemory"].get(); + } + + // Parse CorrType enum + if (paramConfig.contains("CorrType")) { + int corrTypeInt = paramConfig["CorrType"].get(); + params.CorrType = static_cast::MatCorrType>(corrTypeInt); + } + + trackingParams.push_back(params); + } + + LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size()); + return trackingParams; +} + void TrackerDPL::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); auto realt = mTimer.RealTime(); mTimer.Start(false); - // mITSTrackingInterface.updateTimeDependentParams(pc); - // mITSTrackingInterface.run(pc); + + if (!mHitRecoConfig.empty()) { + TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get().c_str(), "READ"); + TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get().c_str(), "READ"); + TTree* hitsTree = hitsFile.Get("o2sim"); + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + TTree* mcHeaderTree = mcHeaderFile.Get("o2sim"); + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + const Long64_t nEvents{hitsTree->GetEntries()}; + LOGP(info, "Starting reconstruction from hits for {} events", nEvents); + + if (mMemoryPool.get() == nullptr) { + mMemoryPool = std::make_shared(); + } + if (mTaskArena.get() == nullptr) { + mTaskArena = std::make_shared(1); /// TODO: make it configurable + } + + o2::trk::TimeFrame<11> timeFrame; + o2::its::TrackerTraits<11> itsTrackerTraits; + o2::its::Tracker<11> itsTracker(&itsTrackerTraits); + timeFrame.setMemoryPool(mMemoryPool); + itsTrackerTraits.setMemoryPool(mMemoryPool); + itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); + itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); + itsTracker.adoptTimeFrame(timeFrame); + itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); + auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + + int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); + + const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; + + // Add primary vertices from MC headers for each ROF + timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup); + // Create tracking parameters from config and set them in the time frame + auto trackingParams = createTrackingParamsFromConfig(); + + itsTrackerTraits.updateTrackingParameters(trackingParams); + + const auto trackingLoopStart = std::chrono::steady_clock::now(); + for (size_t iter{0}; iter < trackingParams.size(); ++iter) { + LOGP(info, "{}", trackingParams[iter].asString()); + timeFrame.initialise(iter, trackingParams[iter], 11, false); + itsTrackerTraits.computeLayerTracklets(iter, -1, -1); + LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); + itsTrackerTraits.computeLayerCells(iter); + LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); + itsTrackerTraits.findCellsNeighbours(iter); + LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); + itsTrackerTraits.findRoads(iter); + LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); + itsTrackerTraits.extendTracks(iter); + } + const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); + LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); + + itsTracker.computeTracksMClabels(); + + // Stream tracks and their MC labels to the output + // Collect all tracks and labels from all ROFs + std::vector allTracks; + std::vector allLabels; + + int totalTracks = 0; + int goodTracks = 0; + int fakeTracks = 0; + + for (int iRof = 0; iRof < nRofs; ++iRof) { + const auto& rofTracks = timeFrame.getTracks(iRof); + const auto& rofLabels = timeFrame.getTracksLabel(iRof); + + allTracks.insert(allTracks.end(), rofTracks.begin(), rofTracks.end()); + allLabels.insert(allLabels.end(), rofLabels.begin(), rofLabels.end()); + + totalTracks += rofTracks.size(); + for (const auto& label : rofLabels) { + if (label.isFake()) { + fakeTracks++; + } else { + goodTracks++; + } + } + } + + LOGP(info, "=== Tracking Summary ==="); + LOGP(info, "Total tracks reconstructed: {}", totalTracks); + LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0); + LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0); + + // Stream tracks and labels to DPL output + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks); + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels); + + LOGP(info, "Tracks and MC labels streamed to output"); + + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(framework::QuitRequest::Me); + } + mTimer.Stop(); LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); } @@ -67,16 +367,11 @@ void TrackerDPL::endOfStream(EndOfStreamContext& ec) LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::GPUDataTypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o2::gpu::gpudatatypes::DeviceType dType) { std::vector inputs; - - // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); - // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); - // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); - - // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + std::vector outputs; + outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); auto ggRequest = std::make_shared(false, // orbitResetTime false, // GRPECS=true false, // GRPLHCIF @@ -85,8 +380,29 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::GPUDataTypes::DeviceType d o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB inputs, true); - std::vector outputs; - outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); + + if (!hitRecoConfig.empty()) { + outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); + return DataProcessorSpec{ + "trk-hits-tracker", + {}, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + hitRecoConfig, + dType)}, + Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}}}}; + } + + inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); + + // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); + // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); + // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); + + // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); + // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "VERTICES", 0, Lifetime::Timeframe); @@ -108,6 +424,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::GPUDataTypes::DeviceType d outputs, AlgorithmSpec{adaptFromTask(ggRequest, useMC, + hitRecoConfig, dType)}, Options{}}; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx index 0f75d42710400..166e6f65b4b2b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx @@ -52,6 +52,7 @@ void customize(std::vector& workflowOptions) {"clusters-from-upstream", VariantType::Bool, false, {"clusters will be provided from upstream, skip clusterizer"}}, {"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"tracking-from-hits-config", VariantType::String, "", {"JSON file with tracking from hits configuration"}}, {"disable-tracking", VariantType::Bool, false, {"disable tracking step"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"use-gpu-workflow", VariantType::Bool, false, {"use GPU workflow (default: false)"}}, @@ -66,8 +67,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); + auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); auto useGpuWF = configcontext.options().get("use-gpu-workflow"); - auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); + auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); @@ -76,5 +78,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); - return o2::trk::reco_workflow::getWorkflow(useMC, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); + return o2::trk::reco_workflow::getWorkflow(useMC, hitRecoConfig, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); } diff --git a/Detectors/Upgrades/ITS3/CMakeLists.txt b/Detectors/Upgrades/ITS3/CMakeLists.txt index 73ad4b9d53e37..bdaf1b4bf4292 100644 --- a/Detectors/Upgrades/ITS3/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/CMakeLists.txt @@ -12,10 +12,10 @@ #add_compile_options(-O0 -g -fPIC -fsanitize=address) #add_link_options(-fsanitize=address) -add_subdirectory(data) add_subdirectory(simulation) add_subdirectory(alignment) add_subdirectory(base) add_subdirectory(workflow) add_subdirectory(reconstruction) add_subdirectory(macros) +add_subdirectory(study) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index 83db7632e72f4..937fa8d2e982c 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -28,9 +28,9 @@ // color: for visulisation namespace o2::its3::constants { -constexpr double cm{1e+2}; // This is the default unit of TGeo so we use this as scale -constexpr double mu{1e-6 * cm}; -constexpr double mm{1e-3 * cm}; +constexpr double cm{1.0}; // This is the default unit of TGeo so we use this as scale +constexpr double mu{1e-4 * cm}; +constexpr double mm{1e-1 * cm}; namespace pixelarray { constexpr double width{9.197 * mm}; @@ -102,13 +102,14 @@ constexpr double lengthSensitive{nRSUs * rsu::length}; namespace carbonfoam { // TODO: Waiting for the further information from WP5(Corrado) -constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? -constexpr double longeronsLength{263 * mm}; // from blueprint constexpr double HringLength{6.0 * mm}; // from blueprint +constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? +constexpr double longeronsLength{segment::length - (2 * HringLength)}; // 263mm from blueprint; overrriden to be consitent constexpr double edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping constexpr double gapBetwHringsLongerons{0.05 * mm}; // from blueprint constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? -constexpr std::array radiusHoles{1.0 * mm, 1.0 * mm, 2.0 * mm}; // what is the radius of the holes for each layer? +constexpr std::array radiusHoles{1.0 * mm, 1.0 * mm, 2.0 * mm}; // TODO what is the radius of the holes for each layer? +constexpr double thicknessOuterFoam{7 * mm}; // TODO: lack of carbon foam radius for layer 2, use 0.7 cm as a temporary value constexpr EColor color{kGray}; } // namespace carbonfoam namespace metalstack @@ -147,8 +148,7 @@ namespace apts { constexpr double pitchX{15.0 * mu}; constexpr double pitchZ{15.0 * mu}; -constexpr double responseUpperLimit{10 * mu}; -constexpr double responseYShift{responseUpperLimit - silicon::thicknessOut}; +constexpr double responseYShift{15.5 * mu}; } // namespace apts namespace moss { @@ -213,6 +213,18 @@ inline bool isDetITS3(T detID) } } // namespace detID + +// services +namespace services +{ +// FIXME these value are hallucinated since this not yet defined +constexpr double thickness{2.2 * mm}; // thickness of structure +constexpr double radiusInner{radiiOuter[2] + carbonfoam::thicknessOuterFoam}; // inner radius of services +constexpr double radiusOuter{radiusInner + thickness}; // outer radius of services +constexpr double length{segment::length + (1 * cm)}; // length +constexpr EColor color{kBlue}; +} // namespace services + } // namespace o2::its3::constants #endif diff --git a/Detectors/Upgrades/ITS3/data/CMakeLists.txt b/Detectors/Upgrades/ITS3/data/CMakeLists.txt deleted file mode 100644 index 7a807fd670370..0000000000000 --- a/Detectors/Upgrades/ITS3/data/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does 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(APTS_RESPONSE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/APTSResponseData.root") - -add_custom_command( - OUTPUT ${APTS_RESPONSE_OUTPUT} - COMMAND ${CMAKE_BINARY_DIR}/stage/bin/o2-alpide-response-generator - -c APTS - -i ${ITSRESPONSE_DIR}/response/ITS3ChipResponseData/AptsResponseData/ - -o ${CMAKE_CURRENT_BINARY_DIR}/ - DEPENDS GenerateAlpideResponse - ${ITSRESPONSE_DIR}/response/ITS3ChipResponseData/AptsResponseData/ - COMMENT "Generating APTSResponseData.root" - VERBATIM -) - -add_custom_target( - GenerateAPTSResponse ALL - DEPENDS ${APTS_RESPONSE_OUTPUT} -) - -install( - FILES ${APTS_RESPONSE_OUTPUT} - DESTINATION "${CMAKE_INSTALL_PREFIX}/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/" -) diff --git a/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C b/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C index 9d352393d6fd9..88b342683ca44 100644 --- a/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C +++ b/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C @@ -66,16 +66,11 @@ std::optional propagateTo(Track& trk, const o2::itsmft::CompClusterExt& ++cTotal; auto chipID = clus.getSensorID(); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; + auto isITS3 = o2::its3::constants::detID::isDetITS3(chipID); const float alpha = o2::its::GeometryTGeo::Instance()->getSensorRefAlpha(clus.getSensorID()); // alpha for the tracking frame const auto locC = o2::its3::ioutils::extractClusterData(clus, pattIt, mDict, sigmaY2, sigmaZ2); // get cluster in sensor local frame with errors - Point3D trkC; - auto isITS3 = o2::its3::constants::detID::isDetITS3(chipID); - if (isITS3) { - trkC = o2::its::GeometryTGeo::Instance()->getT2LMatrixITS3(chipID, alpha) ^ (locC); // cluster position in the tracking frame - } else { - trkC = o2::its::GeometryTGeo::Instance()->getMatrixT2L(chipID) ^ (locC); // cluster position in the tracking frame - } - const auto gloC = o2::its::GeometryTGeo::Instance()->getMatrixL2G(chipID)(locC); // global cluster position + Point3D trkC = o2::its::GeometryTGeo::Instance()->getMatrixT2L(chipID) ^ (locC); // cluster position in the tracking frame + const auto gloC = o2::its::GeometryTGeo::Instance()->getMatrixL2G(chipID)(locC); // global cluster position const auto bz = o2::base::Propagator::Instance()->getNominalBz(); // rotate the parameters to the tracking frame then propagate to the clusters'x diff --git a/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt b/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt index cb6812445283c..6b274e764f276 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt @@ -12,7 +12,6 @@ its3_add_macro(CheckDigitsITS3.C) its3_add_macro(CheckClustersITS3.C) its3_add_macro(CheckTracksITS3.C) -its3_add_macro(CheckDCA.C) its3_add_macro(CreateDictionariesITS3.C) its3_add_macro(buildMatBudLUT.C) its3_add_macro(CheckHits.C) diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C index 996a99d87ecbc..5bc053c516079 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C @@ -22,8 +22,10 @@ #include #include +#include "CCDB/BasicCCDBManager.h" #define ENABLE_UPGRADES #include "ITSMFTSimulation/AlpideSimResponse.h" +#include "ITS3Simulation/ChipSimResponse.h" #include "ITS3Base/SegmentationMosaix.h" #include "fairlogger/Logger.h" @@ -34,61 +36,67 @@ using SegmentationMosaix = o2::its3::SegmentationMosaix; double um2cm(double um) { return um * 1e-4; } double cm2um(double cm) { return cm * 1e+4; } -o2::itsmft::AlpideSimResponse *mAlpSimResp0 = nullptr, - *mAlpSimResp1 = nullptr, - *mAptSimResp1 = nullptr; +std::unique_ptr mAlpSimResp0, mAlpSimResp1, mAptSimResp1; -o2::itsmft::AlpideSimResponse* loadResponse(const std::string& fileName, const std::string& respName) +std::unique_ptr loadResponse(const std::string& path) { - TFile* f = TFile::Open(fileName.data()); - if (!f) { - std::cerr << fileName << " not found" << std::endl; + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + o2::itsmft::AlpideSimResponse* base = cdb.get(path); + if (!base) { + std::cerr << path << " not found in " << '\n'; return nullptr; } - auto resp = (o2::itsmft::AlpideSimResponse*)f->Get(respName.data()); - if (!resp) - std::cerr << respName << " not found in " << fileName << std::endl; - return resp; + return std::make_unique(base); } void LoadRespFunc() { - std::string AptsFile = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; - std::string AlpideFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + cdb.setURL("https://alice-ccdb.cern.ch/"); - mAlpSimResp0 = loadResponse(AlpideFile, "response0"); // Vbb=0V - LOG(info) << "ALPIDE Vbb=0V response" << std::endl; + std::cout << "=====================\n"; + LOGP(info, "ALPIDE Vbb=0V response"); + mAlpSimResp0 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbb0"); // Vbb=0V + mAlpSimResp0->computeCentreFromData(); mAlpSimResp0->print(); - mAlpSimResp1 = loadResponse(AlpideFile, "response1"); // Vbb=-3V - LOG(info) << "ALPIDE Vbb=-3V response" << std::endl; + LOGP(info, "Response Centre {}", mAlpSimResp0->getRespCentreDep()); + std::cout << "=====================\n"; + LOGP(info, "ALPIDE Vbb=-3V response"); + mAlpSimResp1 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbbM3"); // Vbb=-3V + mAlpSimResp1->computeCentreFromData(); mAlpSimResp1->print(); - mAptSimResp1 = loadResponse(AptsFile, "response1"); // APTS - LOG(info) << "APTS response" << std::endl; + LOGP(info, "Response Centre {}", mAlpSimResp1->getRespCentreDep()); + std::cout << "=====================\n"; + LOGP(info, "APTS response"); + mAptSimResp1 = loadResponse("IT3/Calib/APTSResponse"); // APTS + mAptSimResp1->computeCentreFromData(); mAptSimResp1->print(); + LOGP(info, "Response Centre {}", mAptSimResp1->getRespCentreDep()); + std::cout << "=====================\n"; } -std::vector getCollectionSeediciencies(o2::itsmft::AlpideSimResponse* resp, +std::vector getCollectionSeediciencies(o2::its3::ChipSimResponse* resp, const std::vector& depths) { std::vector seed; bool flipRow = false, flipCol = false; for (auto depth : depths) { auto rspmat = resp->getResponse(0.0, 0.0, - um2cm(depth) + resp->getDepthMin() + 1.e-9, + um2cm(depth) + 1.e-9, flipRow, flipCol); seed.push_back(rspmat ? rspmat->getValue(2, 2) : 0.f); } return seed; } -std::vector getShareValues(o2::itsmft::AlpideSimResponse* resp, +std::vector getShareValues(o2::its3::ChipSimResponse* resp, const std::vector& depths) { std::vector share; bool flipRow = false, flipCol = false; for (auto depth : depths) { auto rspmat = resp->getResponse(0.0, 0.0, - um2cm(depth) + resp->getDepthMin() + 1.e-9, + um2cm(depth) + 1.e-9, flipRow, flipCol); float s = 0; int npix = resp->getNPix(); @@ -103,14 +111,14 @@ std::vector getShareValues(o2::itsmft::AlpideSimResponse* resp, return share; } -std::vector getEffValues(o2::itsmft::AlpideSimResponse* resp, +std::vector getEffValues(o2::its3::ChipSimResponse* resp, const std::vector& depths) { std::vector all; bool flipRow = false, flipCol = false; for (auto depth : depths) { auto rspmat = resp->getResponse(0.0, 0.0, - um2cm(depth) + resp->getDepthMin() + 1.e-9, + um2cm(depth) + 1.e-9, flipRow, flipCol); float s = 0; int npix = resp->getNPix(); @@ -129,13 +137,16 @@ void CheckChipResponseFile() LoadRespFunc(); LOG(info) << "Response function loaded" << std::endl; - std::vector vecDepth(50); - for (int i = 0; i < 50; ++i) - vecDepth[i] = i; + std::vector vecDepth; + int numPoints = 100; + for (int i = 0; i < numPoints; ++i) { + float value = -50 + i * (100.0f / (numPoints - 1)); + vecDepth.push_back(value); + } int colors[] = {kOrange + 7, kRed + 1, kAzure + 4}; struct RespInfo { - o2::itsmft::AlpideSimResponse* resp; + std::unique_ptr& resp; std::string title; int color; }; @@ -145,7 +156,7 @@ void CheckChipResponseFile() {mAlpSimResp1, "ALPIDE Vbb=-3V", colors[2]}}; TCanvas* c1 = new TCanvas("c1", "c1", 800, 600); - TH1* frame = c1->DrawFrame(-1, -0.049, 50, 1.049); + TH1* frame = c1->DrawFrame(-50, -0.049, 50, 1.049); frame->SetTitle(";Depth(um);Charge Collection Seed / Share / Eff"); TLegend* leg = new TLegend(0.15, 0.5, 0.4, 0.85); leg->SetFillStyle(0); @@ -154,11 +165,11 @@ void CheckChipResponseFile() for (auto& r : responses) { if (!r.resp) continue; - auto seed = getCollectionSeediciencies(r.resp, vecDepth); - auto shr = getShareValues(r.resp, vecDepth); - auto all = getEffValues(r.resp, vecDepth); + auto seed = getCollectionSeediciencies(r.resp.get(), vecDepth); + auto shr = getShareValues(r.resp.get(), vecDepth); + auto all = getEffValues(r.resp.get(), vecDepth); - TGraph* grSeed = new TGraph(vecDepth.size(), vecDepth.data(), seed.data()); + auto grSeed = new TGraph(vecDepth.size(), vecDepth.data(), seed.data()); grSeed->SetTitle(Form("%s seed", r.title.c_str())); grSeed->SetLineColor(r.color); grSeed->SetLineWidth(2); @@ -168,7 +179,7 @@ void CheckChipResponseFile() grSeed->Draw("SAME LP"); leg->AddEntry(grSeed, Form("%s seed", r.title.c_str()), "lp"); - TGraph* grShare = new TGraph(vecDepth.size(), vecDepth.data(), shr.data()); + auto grShare = new TGraph(vecDepth.size(), vecDepth.data(), shr.data()); grShare->SetLineColor(r.color); grShare->SetLineWidth(2); grShare->SetMarkerColor(r.color); @@ -177,7 +188,7 @@ void CheckChipResponseFile() grShare->Draw("SAME LP"); leg->AddEntry(grShare, Form("%s share", r.title.c_str()), "p"); - TGraph* grEff = new TGraph(vecDepth.size(), vecDepth.data(), all.data()); + auto grEff = new TGraph(vecDepth.size(), vecDepth.data(), all.data()); grEff->SetLineColor(r.color); grEff->SetLineWidth(2); grEff->SetMarkerColor(r.color); diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C b/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C index 006271a1ea7bd..5e56321c7676d 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C @@ -225,28 +225,33 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", locH = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local locHsta = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); - float x0 = locHsta.X(), dltx = locH.X() - x0; - float y0 = locHsta.Y(), dlty = locH.Y() - y0; - float z0 = locHsta.Z(), dltz = locH.Z() - z0; - auto r = (0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff) - y0) / dlty; - - if (!isIB) { - locH.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); + float x0, y0, z0, dltx, dlty, dltz, r; + if (isIB) { + float xFlat{0.}, yFlat{0.}; + mMosaixSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlat, yFlat); + locC.SetCoordinates(xFlat, yFlat, locC.Z()); + mMosaixSegmentations[layer].curvedToFlat(locH.X(), locH.Y(), xFlat, yFlat); + locH.SetCoordinates(xFlat, yFlat, locH.Z()); + mMosaixSegmentations[layer].curvedToFlat(locHsta.X(), locHsta.Y(), xFlat, yFlat); + locHsta.SetCoordinates(xFlat, yFlat, locHsta.Z()); + x0 = locHsta.X(); + dltx = locH.X() - x0; + y0 = locHsta.Y(); + dlty = locH.Y() - y0; + z0 = locHsta.Z(); + dltz = locH.Z() - z0; + r = (o2::its3::constants::pixelarray::pixels::apts::responseYShift - y0) / dlty; } else { - // compare in local flat coordinates - float xFlatEnd{0.}, yFlatEnd{0.}; - mMosaixSegmentations[layer].curvedToFlat(locH.X(), locH.Y(), xFlatEnd, yFlatEnd); - locH.SetXYZ(xFlatEnd, yFlatEnd, locH.Z()); - float xFlatSta{0.}, yFlatSta{0.}; - mMosaixSegmentations[layer].curvedToFlat(locHsta.X(), locHsta.Y(), xFlatSta, yFlatSta); - locHsta.SetXYZ(xFlatSta, yFlatSta, locHsta.Z()); - - // not really precise, but okish - locH.SetXYZ(0.5f * (locH.X() + locHsta.X()), 0.5f * (locH.Y() + locHsta.Y()), 0.5f * (locH.Z() + locHsta.Z())); - - mMosaixSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlatSta, yFlatSta); - locC.SetXYZ(xFlatSta, yFlatSta, locC.Z()); + x0 = locHsta.X(); + dltx = locH.X() - x0; + y0 = locHsta.Y(); + dlty = locH.Y() - y0; + z0 = locHsta.Z(); + dltz = locH.Z() - z0; + r = (0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff) - y0) / dlty; } + locH.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); + float theta = std::acos(gloC.Z() / gloC.Rho()); float eta = -std::log(std::tan(theta / 2)); @@ -278,7 +283,7 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", nt.Draw("cgy:cgx>>h_cgy_vs_cgx_OB(1000, -50, 50, 1000, -50, 50)", "id >= 3456", "colz"); canvCgXCgY->cd(4); nt.Draw("cgy:cgz>>h_cgy_vs_cgz_OB(1000, -100, 100, 1000, -50, 50)", "id >= 3456", "colz"); - canvCgXCgY->SaveAs("it3clusters_y_vs_x_vs_z.pdf"); + canvCgXCgY->SaveAs("it3clusters_y_vs_x_vs_z.png"); auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); canvdXdZ->Divide(2, 2); @@ -290,7 +295,7 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", nt.Draw("dx:dz>>h_dx_vs_dz_IB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id < 3456 && abs(cgz) < 2", "colz"); canvdXdZ->cd(4)->SetLogz(); nt.Draw("dx:dz>>h_dx_vs_dz_OB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id >= 3456 && abs(cgz) < 2", "colz"); - canvdXdZ->SaveAs("it3clusters_dx_vs_dz.pdf"); + canvdXdZ->SaveAs("it3clusters_dx_vs_dz.png"); auto canvCHXZ = new TCanvas("canvCHXZ", "", 1600, 1600); canvCHXZ->Divide(2, 2); @@ -302,7 +307,7 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", nt.Draw("(cgz-hgz)*10000:eta>>h_chz_IB(101,-1.4,1.4,101,-50,50)", "id<3456", "prof"); canvCHXZ->cd(4); nt.Draw("(cgz-hgz)*10000:eta>>h_chz_OB(101,-1.4,1.4,101,-50,50)", "id>=3456", "prof"); - canvCgXCgY->SaveAs("it3clusters_xz_eta.pdf"); + canvCgXCgY->SaveAs("it3clusters_xz_eta.png"); auto c1 = new TCanvas("p1", "pullX"); c1->cd(); diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckDCA.C b/Detectors/Upgrades/ITS3/macros/test/CheckDCA.C deleted file mode 100644 index b2872431384f1..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/test/CheckDCA.C +++ /dev/null @@ -1,965 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CheckDCA.C -/// \brief Simple macro to check ITS3 impact parameter resolution - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DataFormatsITS/TrackITS.h" -#include "DataFormatsTPC/TrackTPC.h" -#include "DetectorsBase/Propagator.h" -#include "Field/MagneticField.h" -#include "ITSBase/GeometryTGeo.h" -#include "DetectorsBase/Propagator.h" -#include "ReconstructionDataFormats/TrackTPCITS.h" -#include "ReconstructionDataFormats/Vertex.h" -#include "ReconstructionDataFormats/DCA.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCEventHeader.h" -#include "SimulationDataFormat/MCTrack.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "SimulationDataFormat/MCUtils.h" -#include "SimulationDataFormat/TrackReference.h" -#include "Steer/MCKinematicsReader.h" - -#include -#include -#include -#include -#include -#include -#include -#endif - -namespace fs = std::filesystem; - -constexpr auto mMatCorr{o2::base::Propagator::MatCorrType::USEMatCorrNONE}; -constexpr float mMaxStep{2}; - -constexpr float rapMax{0.9}; - -std::vector find_dirs(fs::path const& dir, std::function filter, std::optional> sort = std::nullopt) -{ - std::vector result; - if (fs::exists(dir)) { // Find Dirs matching filter - for (auto const& entry : fs::recursive_directory_iterator(dir, fs::directory_options::follow_directory_symlink)) { - if (fs::is_directory(entry) && filter(entry)) { - result.emplace_back(entry); - } - } - } - if (sort) { // Optionally sort paths - std::sort(result.begin(), result.end(), *sort); - } - return result; -} - -void CheckDCA(const std::string& collisioncontextFileName = "collisioncontext.root", - const std::string& tpcTracksFileName = "tpctracks.root", - const std::string& itsTracksFileName = "o2trac_its.root", - const std::string& itstpcTracksFileName = "o2match_itstpc.root", - const std::string& magFileName = "o2sim_grp.root") -{ - gROOT->SetBatch(); - gStyle->SetOptStat(0); - gStyle->SetPalette(kRainBow); - gStyle->SetPadLeftMargin(0.16); - gStyle->SetPadTickX(1); - gStyle->SetPadTickY(1); - gErrorIgnoreLevel = 2001; // suppress warnings - ProcInfo_t procInfo; - - const int nPtBins = 35; - const int nPtBinsEff = 39; - double ptLimits[nPtBins] = {0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2., 2.2, 2.5, 3., 4., 5., 6., 8., 10., 15., 20.}; - double ptLimitsEff[nPtBinsEff] = {0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2., 2.2, 2.5, 3., 4., 5., 6., 8., 10., 15., 20.}; - - const std::regex tf_pattern(R"(tf\d+)"); - auto tf_matcher = [&tf_pattern](fs::path const& p) -> bool { - return std::regex_search(p.string(), tf_pattern); - }; - auto tf_sorter = [&tf_pattern](fs::path const& a, fs::path const& b) -> bool { - const auto &as = a.string(), &bs = b.string(); - std::smatch am, bm; - if (std::regex_search(as, am, tf_pattern) && std::regex_search(bs, bm, tf_pattern)) { - return std::stoi(am.str().substr(2)) < std::stoi(bm.str().substr(2)); - } else { - LOGP(fatal, "TF Regex matching failed"); - return false; - } - }; - - const int nSpecies = 4; - std::array pdgCodes{11, 211, 321, 2212}; - auto fGaus = new TF1("fGaus", "gaus", -200., 200.); - std::map partNames = { - {11, "Electrons"}, - {211, "Pions"}, - {321, "Kaons"}, - {2212, "Protons"}}; - std::map colors{{11, kOrange + 7}, {211, kRed + 1}, {321, kAzure + 4}, {2212, kGreen + 2}}; - /// ITS - std::map hDcaxyResAllLayersITS = { - {11, new TH1F("hDcaxyResElectronsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcaxyResPionsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcaxyResKaonsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcaxyResProtonsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcazResAllLayersITS = { - {11, new TH1F("hDcazResElectronsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcazResPionsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcazResKaonsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcazResProtonsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hPtResAllLayersITS = { - {11, new TH1F("hPtResElectronsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}, - {211, new TH1F("hPtResPionsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}, - {321, new TH1F("hPtResKaonsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hPtResProtonsAllLayersITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}}; - std::map hDcaxyResNoFirstLayerITS = { - {11, new TH1F("hDcaxyResElectronsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcaxyResPionsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcaxyResKaonsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcaxyResProtonsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcazResNoFirstLayerITS = { - {11, new TH1F("hDcazResElectronsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcazResPionsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcazResKaonsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcazResProtonsNoFirstLayerITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcaxyReskAnyITS = { - {11, new TH1F("hDcaxyResElectronskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcaxyResPionskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcaxyResKaonskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcaxyResProtonskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcazReskAnyITS = { - {11, new TH1F("hDcazResElectronskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcazResPionskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcazResKaonskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcazResProtonskAnyITS", "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}}; - - std::map hDcaxyVsPtAllLayersITS = { - {11, new TH2F("hDcaxyVsPtElectronsAllLayersITS", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPtPionsAllLayersITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPtKaonsAllLayersITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPtProtonsAllLayersITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcazVsPtAllLayersITS = { - {11, new TH2F("hDcazVsPtElectronsAllLayersITS", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcazVsPtPionsAllLayersITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcazVsPtKaonsAllLayersITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPtProtonsAllLayersITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcaxyVsPhiAllLayersITS = { - {11, new TH2F("hDcaxyVsPhiElectronsAllLayersITS", "ITS Electrons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPhiPionsAllLayersITS", "ITS Pions (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPhiKaonsAllLayersITS", "ITS Kaons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPhiProtonsAllLayersITS", "ITS Protons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}}; - std::map hDcazVsPhiAllLayersITS = { - {11, new TH2F("hDcazVsPhiElectronsAllLayersITS", "ITS Electrons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {211, new TH2F("hDcazVsPhiPionsAllLayersITS", "ITS Pions (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {321, new TH2F("hDcazVsPhiKaonsAllLayersITS", "ITS Kaons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPhiProtonsAllLayersITS", "ITS Protons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}}; - std::map hDcaxyVsPtNoFirstLayerITS = { - {11, new TH2F("hDcaxyVsPtElectronsNoFirstLayerITS", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPtPionsNoFirstLayerITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPtKaonsNoFirstLayerITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPtProtonsNoFirstLayerITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcazVsPtNoFirstLayerITS = { - {11, new TH2F("hDcazVsPtElectronsNoFirstLayerITS", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcazVsPtPionsNoFirstLayerITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcazVsPtKaonsNoFirstLayerITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPtProtonsNoFirstLayerITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcazVsPtkAnyITS = { - {11, new TH2F("hDcazVsPtElectronskAnyITS ", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcazVsPtPionskAnyITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcazVsPtKaonskAnyITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPtProtonskAnyITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcaxyVsPtkAnyITS = { - {11, new TH2F("hDcaxyVsPtElectronskAnyITS", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPtPionskAnyITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPtKaonskAnyITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPtProtonskAnyITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDeltaPtVsPtAllLayersITS = { - {11, new TH2F("hDeltaPtVsPtElectronsAllLayersITS", "ITS Electrons;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}, - {211, new TH2F("hDeltaPtVsPtPionsAllLayersITS", "ITS Pions;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}, - {321, new TH2F("hDeltaPtVsPtKaonsAllLayersITS", "ITS Kaons;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}, - {2212, new TH2F("hDeltaPtVsPtProtonsAllLayersITS", "ITS Protons;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}}; - // ITS-TPC - std::map hDcaxyResAllLayersITSTPC = { - {11, new TH1F("hDcaxyResElectronsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcaxyResPionsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcaxyResKaonsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcaxyResProtonsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcazResAllLayersITSTPC = { - {11, new TH1F("hDcazResElectronsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcazResPionsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcazResKaonsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcazResProtonsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hPtResAllLayersITSTPC = { - {11, new TH1F("hPtResElectronsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}, - {211, new TH1F("hPtResPionsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}, - {321, new TH1F("hPtResKaonsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hPtResProtonsAllLayersITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits)}}; - std::map hDcaxyResNoFirstLayerITSTPC = { - {11, new TH1F("hDcaxyResElectronsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcaxyResPionsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcaxyResKaonsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcaxyResProtonsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcazResNoFirstLayerITSTPC = { - {11, new TH1F("hDcazResElectronsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcazResPionsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcazResKaonsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcazResProtonsNoFirstLayerITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcaxyReskAnyITSTPC = { - {11, new TH1F("hDcaxyResElectronskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcaxyResPionskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcaxyResKaonskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcaxyResProtonskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits)}}; - std::map hDcazReskAnyITSTPC = { - {11, new TH1F("hDcazResElectronskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {211, new TH1F("hDcazResPionskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {321, new TH1F("hDcazResKaonskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}, - {2212, new TH1F("hDcazResProtonskAnyITSTPC", "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits)}}; - - std::map hDcaxyVsPtAllLayersITSTPC = { - {11, new TH2F("hDcaxyVsPtElectronsAllLayersITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPtPionsAllLayersITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPtKaonsAllLayersITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPtProtonsAllLayersITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcazVsPtAllLayersITSTPC = { - {11, new TH2F("hDcazVsPtElectronsAllLayersITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcazVsPtPionsAllLayersITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcazVsPtKaonsAllLayersITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPtProtonsAllLayersITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcaxyVsPhiAllLayersITSTPC = { - {11, new TH2F("hDcaxyVsPhiElectronsAllLayersITSTPC", "ITS-TPC Electrons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPhiPionsAllLayersITSTPC", "ITS-TPC Pions (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPhiKaonsAllLayersITSTPC", "ITS-TPC Kaons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPhiProtonsAllLayersITSTPC", "ITS-TPC Protons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{xy}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}}; - std::map hDcazVsPhiAllLayersITSTPC = { - {11, new TH2F("hDcazVsPhiElectronsAllLayersITSTPC", "ITS-TPC Electrons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {211, new TH2F("hDcazVsPhiPionsAllLayersITSTPC", "ITS-TPC Pions (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {321, new TH2F("hDcazVsPhiKaonsAllLayersITSTPC", "ITS-TPC Kaons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPhiProtonsAllLayersITSTPC", "ITS-TPC Protons (>2 Gev);#varphi (rad);#sigma(DCA_{#it{z}}) (#mum)", 100, 0.f, 2 * TMath::Pi(), 1000, -500, 500)}}; - std::map hDcaxyVsPtNoFirstLayerITSTPC = { - {11, new TH2F("hDcaxyVsPtElectronsNoFirstLayerITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPtPionsNoFirstLayerITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPtKaonsNoFirstLayerITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPtProtonsNoFirstLayerITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcazVsPtNoFirstLayerITSTPC = { - {11, new TH2F("hDcazVsPtElectronsNoFirstLayerITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcazVsPtPionsNoFirstLayerITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcazVsPtKaonsNoFirstLayerITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPtProtonsNoFirstLayerITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcazVsPtkAnyITSTPC = { - {11, new TH2F("hDcazVsPtElectronskAnyITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcazVsPtPionskAnyITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcazVsPtKaonskAnyITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcazVsPtProtonskAnyITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDcaxyVsPtkAnyITSTPC = { - {11, new TH2F("hDcaxyVsPtElectronskAnyITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {211, new TH2F("hDcaxyVsPtPionskAnyITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {321, new TH2F("hDcaxyVsPtKaonskAnyITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}, - {2212, new TH2F("hDcaxyVsPtProtonskAnyITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", nPtBins - 1, ptLimits, 1000, -500, 500)}}; - std::map hDeltaPtVsPtAllLayersITSTPC = { - {11, new TH2F("hDeltaPtVsPtElectronsAllLayersITSTPC", "ITS-TPC Electrons;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}, - {211, new TH2F("hDeltaPtVsPtPionsAllLayersITSTPC", "ITS-TPC Pions;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}, - {321, new TH2F("hDeltaPtVsPtKaonsAllLayersITSTPC", "ITS-TPC Kaons;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}, - {2212, new TH2F("hDeltaPtVsPtProtonsAllLayersITSTPC", "ITS-TPC Protons;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})", nPtBins - 1, ptLimits, 200, -0.2, 0.2)}}; - - o2::dataformats::VertexBase collision; - o2::dataformats::DCA impactParameter; - - const auto origWD{fs::current_path()}; - const auto tfDirs = find_dirs(fs::current_path(), tf_matcher, tf_sorter); - for (const auto& tfDir : tfDirs) { - LOGP(info, "Analysing {:?}", tfDir.c_str()); - fs::current_path(tfDir); - - // MC Information - o2::steer::MCKinematicsReader mcReader; - if (!mcReader.initFromDigitContext(collisioncontextFileName)) { - LOGP(error, "Cannot init MC reader in {:?}", tfDir.c_str()); - continue; - } - - // Magnetic field and Propagator - float bz{-999}; - static bool initOnce{false}; - if (!initOnce) { - initOnce = true; - o2::base::Propagator::initFieldFromGRP(magFileName); - bz = o2::base::Propagator::Instance()->getNominalBz(); - } - - LOGP(info, "Loading ITS Tracks"); - auto fITSTracks = TFile::Open(itsTracksFileName.c_str(), "READ"); - auto tITSTracks = fITSTracks->Get("o2sim"); - std::vector* itsTracks{nullptr}; - tITSTracks->SetBranchAddress("ITSTrack", &itsTracks); - std::vector* itsTrkLab{nullptr}; - tITSTracks->SetBranchAddress("ITSTrackMCTruth", &itsTrkLab); - - for (Long64_t iEntry{0}; tITSTracks->LoadTree(iEntry) >= 0; ++iEntry) { - tITSTracks->GetEntry(iEntry); - for (size_t iTrk{0}; iTrk < itsTracks->size(); ++iTrk) { - auto trk = itsTracks->at(iTrk); - const auto& lbl = itsTrkLab->at(iTrk); - if (!lbl.isValid()) { - continue; - } - - const auto& mcEvent = mcReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); - const auto& mcTrack = mcReader.getTrack(lbl); - if (!mcTrack->isPrimary() || !(std::abs(mcTrack->GetEta()) < rapMax)) { - continue; - } - auto pdg = std::abs(mcTrack->GetPdgCode()); - if (pdg != 11 && pdg != 211 && pdg != 321 && pdg != 2212) { - continue; - } - - collision.setXYZ(mcEvent.GetX(), mcEvent.GetY(), mcEvent.GetZ()); - if (!o2::base::Propagator::Instance()->propagateToDCA(collision, trk, bz, mMaxStep, mMatCorr, &impactParameter)) { - continue; - } - - auto ptReco = trk.getPt(); - auto ptGen = mcTrack->GetPt(); - auto deltaPt = (1. / ptReco - 1. / ptGen) / (1. / ptGen); - auto dcaXY = impactParameter.getY() * 10000; - auto dcaZ = impactParameter.getZ() * 10000; - auto phiReco = trk.getPhi(); - - if (trk.getNumberOfClusters() == 7) { - hDcaxyVsPtAllLayersITS[pdg]->Fill(ptGen, dcaXY); - hDcazVsPtAllLayersITS[pdg]->Fill(ptGen, dcaZ); - hDeltaPtVsPtAllLayersITS[pdg]->Fill(ptGen, deltaPt); - if (ptGen > 2.) { - hDcaxyVsPhiAllLayersITS[pdg]->Fill(phiReco, dcaXY); - hDcazVsPhiAllLayersITS[pdg]->Fill(phiReco, dcaZ); - } - } else if (!trk.hasHitOnLayer(0)) { - hDcaxyVsPtNoFirstLayerITS[pdg]->Fill(ptGen, dcaXY); - hDcazVsPtNoFirstLayerITS[pdg]->Fill(ptGen, dcaZ); - } else { - hDcaxyVsPtkAnyITS[pdg]->Fill(ptGen, dcaXY); - hDcazVsPtkAnyITS[pdg]->Fill(ptGen, dcaZ); - } - } - } - - LOGP(info, "Loading ITS-TPC Tracks"); - auto fITSTPCTracks = TFile::Open(itstpcTracksFileName.c_str(), "READ"); - auto tITSTPCTracks = fITSTPCTracks->Get("matchTPCITS"); - std::vector* itstpcTracks{nullptr}; - tITSTPCTracks->SetBranchAddress("TPCITS", &itstpcTracks); - std::vector* itstpcTrkLab{nullptr}; - tITSTPCTracks->SetBranchAddress("MatchMCTruth", &itstpcTrkLab); - // TPC Tracks - auto fTPCTracks = TFile::Open(tpcTracksFileName.c_str(), "READ"); - auto tTPCTracks = fTPCTracks->Get("tpcrec"); - std::vector* tpcTracks{nullptr}; - tTPCTracks->SetBranchAddress("TPCTracks", &tpcTracks); - std::vector* tpcTrkLab{nullptr}; - tTPCTracks->SetBranchAddress("TPCTracksMCTruth", &tpcTrkLab); - for (Long64_t iEntry{0}; tITSTPCTracks->LoadTree(iEntry) >= 0; ++iEntry) { - tITSTPCTracks->GetEntry(iEntry); - tITSTracks->GetEntry(iEntry); - tTPCTracks->GetEntry(iEntry); - for (size_t iTrk{0}; iTrk < itstpcTracks->size(); ++iTrk) { - auto trk = itstpcTracks->at(iTrk); - const auto& lbl = itstpcTrkLab->at(iTrk); - - const auto& trkITS = itsTracks->at(trk.getRefITS().getIndex()); - const auto& trkITSLbl = itsTrkLab->at(trk.getRefITS().getIndex()); - const auto& trkTPC = tpcTracks->at(trk.getRefTPC().getIndex()); - const auto& trkTPCLbl = tpcTrkLab->at(trk.getRefTPC().getIndex()); - if (!lbl.isValid() || trkITSLbl != trkTPCLbl) { - continue; - } - - const auto& mcEvent = mcReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); - const auto& mcTrack = mcReader.getTrack(lbl); - if (!mcTrack->isPrimary() || !(std::abs(mcTrack->GetEta()) < rapMax)) { - continue; - } - - auto pdg = std::abs(mcTrack->GetPdgCode()); - if (pdg != 11 && pdg != 211 && pdg != 321 && pdg != 2212) { - continue; - } - - collision.setXYZ(mcEvent.GetX(), mcEvent.GetY(), mcEvent.GetZ()); - if (!o2::base::Propagator::Instance()->propagateToDCA(collision, trk, bz, mMaxStep, mMatCorr, &impactParameter)) { - continue; - } - - auto ptReco = trk.getPt(); - auto ptGen = mcTrack->GetPt(); - auto deltaPt = (1. / ptReco - 1. / ptGen) / (1. / ptGen); - auto dcaXY = impactParameter.getY() * 10000; - auto dcaZ = impactParameter.getZ() * 10000; - auto phiReco = trk.getPhi(); - - if (trkITS.getNumberOfClusters() == 7) { - hDcaxyVsPtAllLayersITSTPC[pdg]->Fill(ptGen, dcaXY); - hDcazVsPtAllLayersITSTPC[pdg]->Fill(ptGen, dcaZ); - hDeltaPtVsPtAllLayersITSTPC[pdg]->Fill(ptGen, deltaPt); - if (ptGen > 2.) { - hDcaxyVsPhiAllLayersITSTPC[pdg]->Fill(phiReco, dcaXY); - hDcazVsPhiAllLayersITSTPC[pdg]->Fill(phiReco, dcaZ); - } - } else if (!trkITS.hasHitOnLayer(0) && !trkITS.hasHitOnLayer(1) && !trkITS.hasHitOnLayer(2)) { - hDcaxyVsPtNoFirstLayerITSTPC[pdg]->Fill(ptGen, dcaXY); - hDcazVsPtNoFirstLayerITSTPC[pdg]->Fill(ptGen, dcaZ); - } else { - hDcaxyVsPtkAnyITSTPC[pdg]->Fill(ptGen, dcaXY); - hDcazVsPtkAnyITSTPC[pdg]->Fill(ptGen, dcaZ); - } - } - } - - delete itsTracks; - delete itsTrkLab; - delete tpcTracks; - delete tpcTrkLab; - delete itstpcTracks; - delete itstpcTrkLab; - delete tITSTracks; - delete tTPCTracks; - delete tITSTPCTracks; - delete fITSTracks; - delete fTPCTracks; - delete fITSTPCTracks; - - gSystem->GetProcInfo(&procInfo); - LOGF(info, "MemVirtual (%ld), MemResident (%ld)", procInfo.fMemVirtual, procInfo.fMemResident); - LOGP(info, "Done with {:?}", tfDir.c_str()); - if (procInfo.fMemResident > 200'000'000) { - LOGP(error, "Exceeding 200GBs stopping!"); - break; - } - } - LOGP(info, "Restoring original CWD to {:?}", origWD.c_str()); - fs::current_path(origWD); // restore original wd - - LOGP(info, "Projecting Plots"); - TH1* hProj; - const char* fitOpt{"QWMER"}; - /* const char* fitOpt{"Q"}; */ - std::map lProjITS = { - {11, new TList()}, - {211, new TList()}, - {321, new TList()}, - {2212, new TList()}, - }; - std::map lProjITSTPC = { - {11, new TList()}, - {211, new TList()}, - {321, new TList()}, - {2212, new TList()}, - }; - for (const auto& pdgCode : pdgCodes) { - for (auto iPt{0}; iPt < nPtBins; ++iPt) { - // ITS - auto ptMin = hDcaxyVsPtAllLayersITS[pdgCode]->GetXaxis()->GetBinLowEdge(iPt + 1); - float minFit = (ptMin < 1.) ? -200. : -50.; - float maxFit = (ptMin < 1.) ? 200. : 50.; - - hProj = hDeltaPtVsPtAllLayersITS[pdgCode]->ProjectionY(Form("hProjDeltaPtAll%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt); - lProjITS[pdgCode]->Add(hProj); - hPtResAllLayersITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hPtResAllLayersITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcaxyVsPtAllLayersITS[pdgCode]->ProjectionY(Form("hProjDcaxyAll%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITS[pdgCode]->Add(hProj); - hDcaxyResAllLayersITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcaxyResAllLayersITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcazVsPtAllLayersITS[pdgCode]->ProjectionY(Form("hProjDcazAll%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITS[pdgCode]->Add(hProj); - hDcazResAllLayersITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcazResAllLayersITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcaxyVsPtNoFirstLayerITS[pdgCode]->ProjectionY(Form("hProjDcaxyNoFirst%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITS[pdgCode]->Add(hProj); - hDcaxyResNoFirstLayerITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcaxyResNoFirstLayerITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcazVsPtNoFirstLayerITS[pdgCode]->ProjectionY(Form("hProjDcazNoFirst%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITS[pdgCode]->Add(hProj); - hDcazResNoFirstLayerITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcazResNoFirstLayerITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcaxyVsPtkAnyITS[pdgCode]->ProjectionY(Form("hProjDcaxyAny%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITS[pdgCode]->Add(hProj); - hDcaxyReskAnyITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcaxyReskAnyITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcazVsPtkAnyITS[pdgCode]->ProjectionY(Form("hProjDcazAny%d__%dITS", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITS[pdgCode]->Add(hProj); - hDcazReskAnyITS[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcazReskAnyITS[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - // ITS-TPC - hProj = hDeltaPtVsPtAllLayersITSTPC[pdgCode]->ProjectionY(Form("hProjDeltaPtAll%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt); - lProjITSTPC[pdgCode]->Add(hProj); - hPtResAllLayersITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hPtResAllLayersITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - ptMin = hDcaxyVsPtAllLayersITSTPC[pdgCode]->GetXaxis()->GetBinLowEdge(iPt + 1); - minFit = (ptMin < 1.) ? -200. : -50.; - maxFit = (ptMin < 1.) ? 200. : 50.; - - hProj = hDcaxyVsPtAllLayersITSTPC[pdgCode]->ProjectionY(Form("hProjDcaxyAll%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITSTPC[pdgCode]->Add(hProj); - hDcaxyResAllLayersITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcaxyResAllLayersITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcazVsPtAllLayersITSTPC[pdgCode]->ProjectionY(Form("hProjDcazAll%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITSTPC[pdgCode]->Add(hProj); - hDcazResAllLayersITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcazResAllLayersITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcaxyVsPtNoFirstLayerITSTPC[pdgCode]->ProjectionY(Form("hProjDcaxyNoFirst%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITSTPC[pdgCode]->Add(hProj); - hDcaxyResNoFirstLayerITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcaxyResNoFirstLayerITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcazVsPtNoFirstLayerITSTPC[pdgCode]->ProjectionY(Form("hProjDcazNoFirst%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITSTPC[pdgCode]->Add(hProj); - hDcazResNoFirstLayerITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcazResNoFirstLayerITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcaxyVsPtkAnyITSTPC[pdgCode]->ProjectionY(Form("hProjDcaxAnty%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITSTPC[pdgCode]->Add(hProj); - hDcaxyReskAnyITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcaxyReskAnyITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - - hProj = hDcazVsPtkAnyITSTPC[pdgCode]->ProjectionY(Form("hProjDcazAny%d__%dITSTPC", pdgCode, iPt), iPt + 1, iPt + 1); - hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); - lProjITSTPC[pdgCode]->Add(hProj); - hDcazReskAnyITSTPC[pdgCode]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); - hDcazReskAnyITSTPC[pdgCode]->SetBinError(iPt + 1, fGaus->GetParError(2)); - } - } - - // Style - LOGP(info, "Styling Plots"); - for (const auto& pdgCode : pdgCodes) { - // ITS - hPtResAllLayersITS[pdgCode]->SetLineWidth(2); - hPtResAllLayersITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hPtResAllLayersITS[pdgCode]->SetLineColor(colors[pdgCode]); - hPtResAllLayersITS[pdgCode]->SetMarkerStyle(kFullCircle); - - hDcaxyResAllLayersITS[pdgCode]->SetLineWidth(2); - hDcaxyResAllLayersITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcaxyResAllLayersITS[pdgCode]->SetLineColor(colors[pdgCode]); - hDcaxyResAllLayersITS[pdgCode]->SetMarkerStyle(kFullCircle); - - hDcazResAllLayersITS[pdgCode]->SetLineWidth(2); - hDcazResAllLayersITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcazResAllLayersITS[pdgCode]->SetLineColor(colors[pdgCode]); - hDcazResAllLayersITS[pdgCode]->SetMarkerStyle(kFullCircle); - - hDcaxyResNoFirstLayerITS[pdgCode]->SetLineWidth(2); - hDcaxyResNoFirstLayerITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcaxyResNoFirstLayerITS[pdgCode]->SetLineColor(colors[pdgCode]); - hDcaxyResNoFirstLayerITS[pdgCode]->SetMarkerStyle(kFullCircle); - - hDcazResNoFirstLayerITS[pdgCode]->SetLineWidth(2); - hDcazResNoFirstLayerITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcazResNoFirstLayerITS[pdgCode]->SetLineColor(colors[pdgCode]); - hDcazResNoFirstLayerITS[pdgCode]->SetMarkerStyle(kFullCircle); - - hDcaxyReskAnyITS[pdgCode]->SetLineWidth(2); - hDcaxyReskAnyITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcaxyReskAnyITS[pdgCode]->SetLineColor(colors[pdgCode]); - hDcaxyReskAnyITS[pdgCode]->SetMarkerStyle(kFullCircle); - - hDcazReskAnyITS[pdgCode]->SetLineWidth(2); - hDcazReskAnyITS[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcazReskAnyITS[pdgCode]->SetLineColor(colors[pdgCode]); - hDcazReskAnyITS[pdgCode]->SetMarkerStyle(kFullCircle); - - // ITS-TPC - hPtResAllLayersITSTPC[pdgCode]->SetLineWidth(2); - hPtResAllLayersITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hPtResAllLayersITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hPtResAllLayersITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - - hDcaxyResAllLayersITSTPC[pdgCode]->SetLineWidth(2); - hDcaxyResAllLayersITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcaxyResAllLayersITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hDcaxyResAllLayersITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - - hDcazResAllLayersITSTPC[pdgCode]->SetLineWidth(2); - hDcazResAllLayersITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcazResAllLayersITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hDcazResAllLayersITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - - hDcaxyResNoFirstLayerITSTPC[pdgCode]->SetLineWidth(2); - hDcaxyResNoFirstLayerITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcaxyResNoFirstLayerITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hDcaxyResNoFirstLayerITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - - hDcazResNoFirstLayerITSTPC[pdgCode]->SetLineWidth(2); - hDcazResNoFirstLayerITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcazResNoFirstLayerITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hDcazResNoFirstLayerITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - - hDcaxyReskAnyITSTPC[pdgCode]->SetLineWidth(2); - hDcaxyReskAnyITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcaxyReskAnyITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hDcaxyReskAnyITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - - hDcazReskAnyITSTPC[pdgCode]->SetLineWidth(2); - hDcazReskAnyITSTPC[pdgCode]->SetMarkerColor(colors[pdgCode]); - hDcazReskAnyITSTPC[pdgCode]->SetLineColor(colors[pdgCode]); - hDcazReskAnyITSTPC[pdgCode]->SetMarkerStyle(kOpenCircle); - } - - /// Output - LOGP(info, "Writing final output"); - // ITS - auto canvPtDeltaITS = new TCanvas("canvPtDeltaITS", "", 1500, 500); - canvPtDeltaITS->Divide(nSpecies, 1); - canvPtDeltaITS->cd(1)->SetLogz(); - hDeltaPtVsPtAllLayersITS[11]->Draw("colz"); - canvPtDeltaITS->cd(2)->SetLogz(); - hDeltaPtVsPtAllLayersITS[211]->Draw("colz"); - canvPtDeltaITS->cd(3)->SetLogz(); - hDeltaPtVsPtAllLayersITS[321]->Draw("colz"); - canvPtDeltaITS->cd(4)->SetLogz(); - hDeltaPtVsPtAllLayersITS[2212]->Draw("colz"); - - auto canvDcaVsPtITS = new TCanvas("canvDcaVsPtITS", "", 1500, 1000); - canvDcaVsPtITS->Divide(nSpecies, 2); - canvDcaVsPtITS->cd(1)->SetLogz(); - hDcaxyVsPtAllLayersITS[11]->Draw("colz"); - canvDcaVsPtITS->cd(2)->SetLogz(); - hDcaxyVsPtAllLayersITS[211]->Draw("colz"); - canvDcaVsPtITS->cd(3)->SetLogz(); - hDcaxyVsPtAllLayersITS[321]->Draw("colz"); - canvDcaVsPtITS->cd(4)->SetLogz(); - hDcaxyVsPtAllLayersITS[2212]->Draw("colz"); - canvDcaVsPtITS->cd(5)->SetLogz(); - hDcazVsPtAllLayersITS[11]->Draw("colz"); - canvDcaVsPtITS->cd(6)->SetLogz(); - hDcazVsPtAllLayersITS[211]->Draw("colz"); - canvDcaVsPtITS->cd(7)->SetLogz(); - hDcazVsPtAllLayersITS[321]->Draw("colz"); - canvDcaVsPtITS->cd(8)->SetLogz(); - hDcazVsPtAllLayersITS[2212]->Draw("colz"); - - auto canvPtResITS = new TCanvas("canvPtResITS", "", 500, 500); - canvPtResITS->DrawFrame(ptLimits[0], 0., ptLimits[nPtBins - 1], 0.2, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})"); - for (const auto& pdgCode : pdgCodes) { - hPtResAllLayersITS[pdgCode]->Draw("same"); - } - - auto canvDcaxyResITS = new TCanvas("canvDcaxyResITS", "", 500, 500); - canvDcaxyResITS->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyResITS->SetLogx(); - canvDcaxyResITS->SetLogy(); - canvDcaxyResITS->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcaxyResAllLayersITS[pdgCode]->Draw("same"); - } - - auto canvDcazResITS = new TCanvas("canvDcazResITS", "", 500, 500); - canvDcazResITS->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazResITS->SetLogx(); - canvDcazResITS->SetLogy(); - canvDcazResITS->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcazResAllLayersITS[pdgCode]->Draw("same"); - } - - auto canvDcaxyResNoFirstLayerITS = new TCanvas("canvDcaxyResNoFirstLayerITS", "", 500, 500); - canvDcaxyResNoFirstLayerITS->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyResNoFirstLayerITS->SetLogx(); - canvDcaxyResNoFirstLayerITS->SetLogy(); - canvDcaxyResNoFirstLayerITS->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcaxyResNoFirstLayerITS[pdgCode]->Draw("same"); - } - - auto canvDcazResNoFirstLayerITS = new TCanvas("canvDcazResNoFirstLayerITS", "", 500, 500); - canvDcazResNoFirstLayerITS->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazResNoFirstLayerITS->SetLogx(); - canvDcazResNoFirstLayerITS->SetLogy(); - canvDcazResNoFirstLayerITS->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcazResNoFirstLayerITS[pdgCode]->Draw("same"); - } - - auto canvDcaxyReskAnyITS = new TCanvas("canvDcaxyReskAnyITS", "", 500, 500); - canvDcaxyReskAnyITS->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyReskAnyITS->SetLogx(); - canvDcaxyReskAnyITS->SetLogy(); - canvDcaxyReskAnyITS->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcaxyReskAnyITS[pdgCode]->Draw("same"); - } - - auto canvDcazReskAnyITS = new TCanvas("canvDcazReskAnyITS", "", 500, 500); - canvDcazReskAnyITS->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazReskAnyITS->SetLogx(); - canvDcazReskAnyITS->SetLogy(); - canvDcazReskAnyITS->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcazReskAnyITS[pdgCode]->Draw("same"); - } - - // ITS-TPC - auto canvPtDeltaITSTPC = new TCanvas("canvPtDeltaITSTPC", "", 1500, 500); - canvPtDeltaITSTPC->Divide(nSpecies, 1); - canvPtDeltaITSTPC->cd(1)->SetLogz(); - hDeltaPtVsPtAllLayersITSTPC[11]->Draw("colz"); - canvPtDeltaITSTPC->cd(2)->SetLogz(); - hDeltaPtVsPtAllLayersITSTPC[211]->Draw("colz"); - canvPtDeltaITSTPC->cd(3)->SetLogz(); - hDeltaPtVsPtAllLayersITSTPC[321]->Draw("colz"); - canvPtDeltaITSTPC->cd(4)->SetLogz(); - hDeltaPtVsPtAllLayersITSTPC[2212]->Draw("colz"); - - auto canvDcaVsPtITSTPC = new TCanvas("canvDcaVsPtITSTPC", "", 1500, 1000); - canvDcaVsPtITSTPC->Divide(nSpecies, 2); - canvDcaVsPtITSTPC->cd(1)->SetLogz(); - hDcaxyVsPtAllLayersITSTPC[11]->Draw("colz"); - canvDcaVsPtITSTPC->cd(2)->SetLogz(); - hDcaxyVsPtAllLayersITSTPC[211]->Draw("colz"); - canvDcaVsPtITSTPC->cd(3)->SetLogz(); - hDcaxyVsPtAllLayersITSTPC[321]->Draw("colz"); - canvDcaVsPtITSTPC->cd(4)->SetLogz(); - hDcaxyVsPtAllLayersITSTPC[2212]->Draw("colz"); - canvDcaVsPtITSTPC->cd(5)->SetLogz(); - hDcazVsPtAllLayersITSTPC[11]->Draw("colz"); - canvDcaVsPtITSTPC->cd(6)->SetLogz(); - hDcazVsPtAllLayersITSTPC[211]->Draw("colz"); - canvDcaVsPtITSTPC->cd(7)->SetLogz(); - hDcazVsPtAllLayersITSTPC[321]->Draw("colz"); - canvDcaVsPtITSTPC->cd(8)->SetLogz(); - hDcazVsPtAllLayersITSTPC[2212]->Draw("colz"); - - auto canvPtResITSTPC = new TCanvas("canvPtResITSTPC", "", 500, 500); - canvPtResITSTPC->DrawFrame(ptLimits[0], 0., ptLimits[nPtBins - 1], 0.2, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})"); - for (const auto& pdgCode : pdgCodes) { - hPtResAllLayersITSTPC[pdgCode]->Draw("same"); - } - - auto canvDcaxyResITSTPC = new TCanvas("canvDcaxyResITSTPC", "", 500, 500); - canvDcaxyResITSTPC->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyResITSTPC->SetLogx(); - canvDcaxyResITSTPC->SetLogy(); - canvDcaxyResITSTPC->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcaxyResAllLayersITSTPC[pdgCode]->Draw("same"); - } - - auto canvDcazResITSTPC = new TCanvas("canvDcazResITSTPC", "", 500, 500); - canvDcazResITSTPC->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazResITSTPC->SetLogx(); - canvDcazResITSTPC->SetLogy(); - canvDcazResITSTPC->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcazResAllLayersITSTPC[pdgCode]->Draw("same"); - } - - auto canvDcaxyResNoFirstLayerITSTPC = new TCanvas("canvDcaxyResNoFirstLayerITSTPC", "", 500, 500); - canvDcaxyResNoFirstLayerITSTPC->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyResNoFirstLayerITSTPC->SetLogx(); - canvDcaxyResNoFirstLayerITSTPC->SetLogy(); - canvDcaxyResNoFirstLayerITSTPC->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcaxyResNoFirstLayerITSTPC[pdgCode]->Draw("same"); - } - - auto canvDcazResNoFirstLayerITSTPC = new TCanvas("canvDcazResNoFirstLayerITSTPC", "", 500, 500); - canvDcazResNoFirstLayerITSTPC->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazResNoFirstLayerITSTPC->SetLogx(); - canvDcazResNoFirstLayerITSTPC->SetLogy(); - canvDcazResNoFirstLayerITSTPC->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcazResNoFirstLayerITSTPC[pdgCode]->Draw("same"); - } - - auto canvDcaxyReskAnyITSTPC = new TCanvas("canvDcaxyReskAnyITSTPC", "", 500, 500); - canvDcaxyReskAnyITSTPC->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyReskAnyITSTPC->SetLogx(); - canvDcaxyReskAnyITSTPC->SetLogy(); - canvDcaxyReskAnyITSTPC->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcaxyReskAnyITSTPC[pdgCode]->Draw("same"); - } - - auto canvDcazReskAnyITSTPC = new TCanvas("canvDcazReskAnyITSTPC", "", 500, 500); - canvDcazReskAnyITSTPC->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS-TPC;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazReskAnyITSTPC->SetLogx(); - canvDcazReskAnyITSTPC->SetLogy(); - canvDcazReskAnyITSTPC->SetGrid(); - for (const auto& pdgCode : pdgCodes) { - hDcazReskAnyITSTPC[pdgCode]->Draw("same"); - } - - // Compare ITS-TPC resolution; - auto canvDcaxyResComp = new TCanvas("canvDcaxyResAllLayersComp", "", 500, 500); - canvDcaxyResComp->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS vs. ITS-TPC (all layers);#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)"); - canvDcaxyResComp->SetLogx(); - canvDcaxyResComp->SetLogy(); - canvDcaxyResComp->SetGrid(); - hDcaxyResAllLayersITS[211]->Draw("same"); - hDcaxyResAllLayersITSTPC[211]->Draw("same"); - gPad->BuildLegend(0.8, 0.8, 0.94, 0.94); - - auto canvDcazResComp = new TCanvas("canvDcazResAllLayersComp", "", 500, 500); - canvDcazResComp->DrawFrame(ptLimits[0], 1., ptLimits[nPtBins - 1], 1.e3, "ITS vs. ITS-TPC (all layers);#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)"); - canvDcazResComp->SetLogx(); - canvDcazResComp->SetLogy(); - canvDcazResComp->SetGrid(); - hDcazResAllLayersITS[211]->Draw("same"); - hDcazResAllLayersITSTPC[211]->Draw("same"); - gPad->BuildLegend(0.8, 0.8, 0.94, 0.94); - - auto canvPtResComp = new TCanvas("canvPtResAllLayersComp", "", 500, 500); - canvPtResComp->DrawFrame(ptLimits[0], 0., ptLimits[nPtBins - 1], 0.2, "ITS vs. ITS-TPC (all layers);#it{p}_{T} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T})"); - canvPtResComp->SetLogx(); - canvPtResComp->SetGrid(); - hPtResAllLayersITS[211]->Draw("same"); - hPtResAllLayersITSTPC[211]->Draw("same"); - gPad->BuildLegend(0.8, 0.8, 0.94, 0.94); - - auto canvDcaPtComp = new TCanvas("canvDcaPtAllLayersComp", "", 500, 500); - canvDcaPtComp->Divide(2, 2); - canvDcaPtComp->cd(1); - hDcaxyVsPtAllLayersITS[211]->Draw(); - canvDcaPtComp->cd(2); - hDcazVsPtAllLayersITS[211]->Draw(); - canvDcaPtComp->cd(3); - hDcaxyVsPtAllLayersITSTPC[211]->Draw(); - canvDcaPtComp->cd(4); - hDcazVsPtAllLayersITSTPC[211]->Draw(); - - auto canvDcaPhiComp = new TCanvas("canvDcaPhiAllLayersComp", "", 500, 500); - canvDcaPhiComp->Divide(2, 2); - canvDcaPhiComp->cd(1); - hDcaxyVsPhiAllLayersITS[211]->Draw(); - canvDcaPhiComp->cd(2); - hDcazVsPhiAllLayersITS[211]->Draw(); - canvDcaPhiComp->cd(3); - hDcaxyVsPhiAllLayersITSTPC[211]->Draw(); - canvDcaPhiComp->cd(4); - hDcazVsPhiAllLayersITSTPC[211]->Draw(); - - // Write - TFile outFile("checkDCA.root", "RECREATE"); - outFile.mkdir("ITS"); - outFile.cd("ITS"); - gDirectory->WriteTObject(canvPtResITS); - gDirectory->WriteTObject(canvDcaxyResITS); - gDirectory->WriteTObject(canvDcazResITS); - gDirectory->WriteTObject(canvDcazResNoFirstLayerITS); - gDirectory->WriteTObject(canvDcaxyResNoFirstLayerITS); - gDirectory->WriteTObject(canvDcaxyReskAnyITS); - gDirectory->WriteTObject(canvDcazReskAnyITS); - gDirectory->WriteTObject(canvPtDeltaITS); - gDirectory->WriteTObject(canvDcaVsPtITS); - - outFile.mkdir("ITS-TPC"); - outFile.cd("ITS-TPC"); - gDirectory->WriteTObject(canvPtResITSTPC); - gDirectory->WriteTObject(canvDcaxyResITSTPC); - gDirectory->WriteTObject(canvDcazResITSTPC); - gDirectory->WriteTObject(canvDcazResNoFirstLayerITSTPC); - gDirectory->WriteTObject(canvDcaxyResNoFirstLayerITSTPC); - gDirectory->WriteTObject(canvDcaxyReskAnyITSTPC); - gDirectory->WriteTObject(canvDcazReskAnyITSTPC); - gDirectory->WriteTObject(canvPtDeltaITSTPC); - gDirectory->WriteTObject(canvDcaVsPtITSTPC); - - outFile.mkdir("Compare"); - outFile.cd("Compare"); - gDirectory->WriteTObject(canvDcaxyResComp); - gDirectory->WriteTObject(canvDcazResComp); - gDirectory->WriteTObject(canvPtResComp); - gDirectory->WriteTObject(canvDcaPtComp); - gDirectory->WriteTObject(canvDcaPhiComp); - - for (const auto& pdgCode : pdgCodes) { - const char* dirName = partNames[pdgCode].c_str(); - auto dir = outFile.mkdir(dirName); - outFile.cd(dirName); - - gDirectory->mkdir("ITS"); - gDirectory->cd("ITS"); - gDirectory->WriteTObject(hDeltaPtVsPtAllLayersITS[pdgCode]); - gDirectory->WriteTObject(hDcaxyVsPtAllLayersITS[pdgCode]); - gDirectory->WriteTObject(hDcazVsPtAllLayersITS[pdgCode]); - gDirectory->WriteTObject(hDcazResAllLayersITS[pdgCode]); - gDirectory->WriteTObject(hDcaxyResAllLayersITS[pdgCode]); - gDirectory->WriteTObject(hDcaxyVsPtNoFirstLayerITS[pdgCode]); - gDirectory->WriteTObject(hDcazVsPtNoFirstLayerITS[pdgCode]); - gDirectory->WriteTObject(hDcazResNoFirstLayerITS[pdgCode]); - gDirectory->WriteTObject(hDcaxyResNoFirstLayerITS[pdgCode]); - gDirectory->WriteTObject(hDcaxyVsPhiAllLayersITS[pdgCode]); - gDirectory->WriteTObject(hDcazVsPhiAllLayersITS[pdgCode]); - gDirectory->mkdir("projections"); - gDirectory->cd("projections"); - for (TObject* obj : *lProjITS[pdgCode]) { - obj->Write(); - } - - dir->cd(); - gDirectory->mkdir("ITS-TPC"); - gDirectory->cd("ITS-TPC"); - gDirectory->WriteTObject(hDeltaPtVsPtAllLayersITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcaxyVsPtAllLayersITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcazVsPtAllLayersITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcazResAllLayersITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcaxyResAllLayersITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcaxyVsPtNoFirstLayerITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcazVsPtNoFirstLayerITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcazResNoFirstLayerITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcaxyResNoFirstLayerITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcaxyVsPhiAllLayersITSTPC[pdgCode]); - gDirectory->WriteTObject(hDcazVsPhiAllLayersITSTPC[pdgCode]); - gDirectory->mkdir("projections"); - gDirectory->cd("projections"); - for (TObject* obj : *lProjITSTPC[pdgCode]) { - obj->Write(); - } - } -} diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C index 240b1bd344af5..82578cc406f0c 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C @@ -40,7 +40,7 @@ #include "fairlogger/Logger.h" #endif -void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfile = "o2sim_HitsIT3.root", std::string inputGeom = "", bool batch = false) +void CheckDigitsITS3(bool readFromFile = false, std::string digifile = "it3digits.root", std::string hitfile = "o2sim_HitsIT3.root", std::string inputGeom = "", bool batch = false) { gROOT->SetBatch(batch); gStyle->SetPalette(kRainBow); @@ -53,176 +53,211 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil using o2::itsmft::SegmentationAlpide; std::array mMosaixSegmentations{0, 1, 2}; - TFile* f = TFile::Open("CheckDigits.root", "recreate"); - TNtuple* nt = new TNtuple("ntd", "digit ntuple", "id:x:y:z:rowD:colD:rowH:colH:xlH:zlH:xlcH:zlcH:dx:dz"); + TFile* f{nullptr}; + TNtuple* nt{nullptr}; + if (!readFromFile) { + f = TFile::Open("CheckDigits.root", "recreate"); + nt = new TNtuple("ntd", "digit ntuple", "id:x:y:z:rowD:colD:rowH:colH:xlH:zlH:xlcH:zlcH:dx:dz:etaH"); - // Geometry - o2::base::GeometryManager::loadGeometry(inputGeom); - auto* gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto* gman = o2::its::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); - // Hits - TFile* hitFile = TFile::Open(hitfile.data()); - TTree* hitTree = (TTree*)hitFile->Get("o2sim"); - int nevH = hitTree->GetEntries(); // hits are stored as one event per entry - std::vector*> hitArray(nevH, nullptr); - std::vector> mc2hitVec(nevH); + // Hits + TFile* hitFile = TFile::Open(hitfile.data()); + TTree* hitTree = (TTree*)hitFile->Get("o2sim"); + int nevH = hitTree->GetEntries(); // hits are stored as one event per entry + std::vector*> hitArray(nevH, nullptr); + std::vector> mc2hitVec(nevH); - // Digits - TFile* digFile = TFile::Open(digifile.data()); - TTree* digTree = (TTree*)digFile->Get("o2sim"); + // Digits + TFile* digFile = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)digFile->Get("o2sim"); - std::vector* digArr = nullptr; - digTree->SetBranchAddress("IT3Digit", &digArr); + std::vector* digArr = nullptr; + digTree->SetBranchAddress("IT3Digit", &digArr); - o2::dataformats::IOMCTruthContainerView* plabels = nullptr; - digTree->SetBranchAddress("IT3DigitMCTruth", &plabels); + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; + digTree->SetBranchAddress("IT3DigitMCTruth", &plabels); - int nevD = digTree->GetEntries(); // digits in cont. readout may be grouped as few events per entry + int nevD = digTree->GetEntries(); // digits in cont. readout may be grouped as few events per entry - int nDigitReadIB{0}, nDigitReadOB{0}; - int nDigitFilledIB{0}, nDigitFilledOB{0}; + int nDigitReadIB{0}, nDigitReadOB{0}; + int nDigitFilledIB{0}, nDigitFilledOB{0}; - // Get Read Out Frame arrays - std::vector* ROFRecordArrray = nullptr; - digTree->SetBranchAddress("IT3DigitROF", &ROFRecordArrray); - std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + // Get Read Out Frame arrays + std::vector* ROFRecordArrray = nullptr; + digTree->SetBranchAddress("IT3DigitROF", &ROFRecordArrray); + std::vector& ROFRecordArrrayRef = *ROFRecordArrray; - std::vector* MC2ROFRecordArrray = nullptr; - digTree->SetBranchAddress("IT3DigitMC2ROF", &MC2ROFRecordArrray); - std::vector& MC2ROFRecordArrrayRef = *MC2ROFRecordArrray; + std::vector* MC2ROFRecordArrray = nullptr; + digTree->SetBranchAddress("IT3DigitMC2ROF", &MC2ROFRecordArrray); + std::vector& MC2ROFRecordArrrayRef = *MC2ROFRecordArrray; - digTree->GetEntry(0); + digTree->GetEntry(0); - int nROFRec = (int)ROFRecordArrrayRef.size(); - std::vector mcEvMin(nROFRec, hitTree->GetEntries()); - std::vector mcEvMax(nROFRec, -1); - o2::dataformats::ConstMCTruthContainer labels; - plabels->copyandflatten(labels); - delete plabels; + int nROFRec = (int)ROFRecordArrrayRef.size(); + std::vector mcEvMin(nROFRec, hitTree->GetEntries()); + std::vector mcEvMax(nROFRec, -1); + o2::dataformats::ConstMCTruthContainer labels; + plabels->copyandflatten(labels); + delete plabels; - LOGP(debug, "Build min and max MC events used by each ROF"); - for (int imc = MC2ROFRecordArrrayRef.size(); imc--;) { - const auto& mc2rof = MC2ROFRecordArrrayRef[imc]; - /* LOGP(debug, "MCRecord: {}", mc2rof.asString()); */ + LOGP(debug, "Build min and max MC events used by each ROF"); + for (int imc = MC2ROFRecordArrrayRef.size(); imc--;) { + const auto& mc2rof = MC2ROFRecordArrrayRef[imc]; + /* LOGP(debug, "MCRecord: {}", mc2rof.asString()); */ - if (mc2rof.rofRecordID < 0) { - continue; // this MC event did not contribute to any ROF - } + if (mc2rof.rofRecordID < 0) { + continue; // this MC event did not contribute to any ROF + } - for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { - int irof = mc2rof.rofRecordID + irfd; + int irof = mc2rof.rofRecordID + irfd; - if (irof >= nROFRec) { - LOGP(error, "ROF={} from MC2ROF record is >= N ROFs={}", irof, nROFRec); - } - if (mcEvMin[irof] > imc) { - mcEvMin[irof] = imc; - } - if (mcEvMax[irof] < imc) { - mcEvMax[irof] = imc; + if (irof >= nROFRec) { + LOGP(error, "ROF={} from MC2ROF record is >= N ROFs={}", irof, nROFRec); + } + if (mcEvMin[irof] > imc) { + mcEvMin[irof] = imc; + } + if (mcEvMax[irof] < imc) { + mcEvMax[irof] = imc; + } } } - } - LOGP(debug, "LOOP on: ROFRecord array"); - unsigned int rofIndex = 0; - unsigned int rofNEntries = 0; - for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { + LOGP(debug, "LOOP on: ROFRecord array"); + unsigned int rofIndex = 0; + unsigned int rofNEntries = 0; + for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { - rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); - rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); + rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); + rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); - // >> read and map MC events contributing to this ROF - for (int im = mcEvMin[iROF]; im <= mcEvMax[iROF]; im++) { + // >> read and map MC events contributing to this ROF + for (int im = mcEvMin[iROF]; im <= mcEvMax[iROF]; im++) { - if (hitArray[im] == nullptr) { + if (hitArray[im] == nullptr) { - hitTree->SetBranchAddress("IT3Hit", &hitArray[im]); - hitTree->GetEntry(im); + hitTree->SetBranchAddress("IT3Hit", &hitArray[im]); + hitTree->GetEntry(im); - auto& mc2hit = mc2hitVec[im]; + auto& mc2hit = mc2hitVec[im]; - for (size_t ih = hitArray[im]->size(); ih--;) { - const auto& hit = (*hitArray[im])[ih]; - uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); - mc2hit.emplace(key, ih); + for (size_t ih = hitArray[im]->size(); ih--;) { + const auto& hit = (*hitArray[im])[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } } } - } - - LOGP(debug, " `-> LOOP on: Digits array(size={}) starting at ROFIndex={} to {}", digArr->size(), rofIndex, rofIndex + rofNEntries); - for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { - int ix = (*digArr)[iDigit].getRow(), iz = (*digArr)[iDigit].getColumn(); - auto chipID = (*digArr)[iDigit].getChipIndex(); - auto layer = its3::constants::detID::getDetID2Layer(chipID); - bool isIB{its3::constants::detID::isDetITS3(chipID)}; - float x{0.f}, y{0.f}, z{0.f}; - (isIB) ? ++nDigitReadIB : ++nDigitReadOB; - - if (isIB) { - // ITS3 IB - float xFlat{0.f}, yFlat{0.f}; - mMosaixSegmentations[layer].detectorToLocal(ix, iz, xFlat, z); - mMosaixSegmentations[layer].flatToCurved(xFlat, 0., x, y); - } else { - // ITS2 OB - SegmentationAlpide::detectorToLocal(ix, iz, x, z); - } - o2::math_utils::Point3D locD(x, y, z); - auto lab = (labels.getLabels(iDigit))[0]; - int trID = lab.getTrackID(); - if (!lab.isValid()) { // not noise - continue; - } - - // get MC info - uint64_t key = (uint64_t(trID) << 32) + chipID; - const auto* mc2hit = &mc2hitVec[lab.getEventID()]; - const auto& hitEntry = mc2hit->find(key); - if (hitEntry == mc2hit->end()) { - LOGP(debug, "Failed to find MC hit entry for Tr {} chipID {}", trID, chipID); - continue; - } + LOGP(debug, " `-> LOOP on: Digits array(size={}) starting at ROFIndex={} to {}", digArr->size(), rofIndex, rofIndex + rofNEntries); + for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { + int ix = (*digArr)[iDigit].getRow(), iz = (*digArr)[iDigit].getColumn(); + auto chipID = (*digArr)[iDigit].getChipIndex(); + auto layer = its3::constants::detID::getDetID2Layer(chipID); + bool isIB{its3::constants::detID::isDetITS3(chipID)}; + float x{0.f}, y{0.f}, z{0.f}; + (isIB) ? ++nDigitReadIB : ++nDigitReadOB; + + if (isIB) { + // ITS3 IB + float xFlat{0.f}, yFlat{0.f}; + mMosaixSegmentations[layer].detectorToLocal(ix, iz, xFlat, z); + mMosaixSegmentations[layer].flatToCurved(xFlat, 0., x, y); + } else { + // ITS2 OB + SegmentationAlpide::detectorToLocal(ix, iz, x, z); + } - auto gloD = gman->getMatrixL2G(chipID)(locD); // convert to global - - ////// HITS - Hit& hit = (*hitArray[lab.getEventID()])[hitEntry->second]; - - auto xyzLocE = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local - auto xyzLocS = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); - o2::math_utils::Vector3D xyzLocM; - xyzLocM.SetCoordinates(0.5f * (xyzLocE.X() + xyzLocS.X()), 0.5f * (xyzLocE.Y() + xyzLocS.Y()), 0.5f * (xyzLocE.Z() + xyzLocS.Z())); - float xlc = 0., zlc = 0.; - int row = 0, col = 0; - - if (isIB) { - float xFlat{0.}, yFlat{0.}; - mMosaixSegmentations[layer].curvedToFlat(xyzLocM.X(), xyzLocM.Y(), xFlat, yFlat); - xyzLocM.SetCoordinates(xFlat, yFlat, xyzLocM.Z()); - mMosaixSegmentations[layer].curvedToFlat(locD.X(), locD.Y(), xFlat, yFlat); - locD.SetCoordinates(xFlat, yFlat, locD.Z()); - if (auto v1 = !mMosaixSegmentations[layer].localToDetector(xyzLocM.X(), xyzLocM.Z(), row, col), - v2 = !mMosaixSegmentations[layer].detectorToLocal(row, col, xlc, zlc); - v1 || v2) { + o2::math_utils::Point3D locD(x, y, z); + auto lab = (labels.getLabels(iDigit))[0]; + int trID = lab.getTrackID(); + if (!lab.isValid()) { // not noise continue; } - } else { - if (auto v1 = !SegmentationAlpide::localToDetector(xyzLocM.X(), xyzLocM.Z(), row, col), - v2 = !SegmentationAlpide::detectorToLocal(row, col, xlc, zlc); - v1 || v2) { + + // get MC info + uint64_t key = (uint64_t(trID) << 32) + chipID; + const auto* mc2hit = &mc2hitVec[lab.getEventID()]; + const auto& hitEntry = mc2hit->find(key); + if (hitEntry == mc2hit->end()) { + LOGP(debug, "Failed to find MC hit entry for Tr {} chipID {}", trID, chipID); continue; } - } - nt->Fill(chipID, gloD.X(), gloD.Y(), gloD.Z(), ix, iz, row, col, xyzLocM.X(), xyzLocM.Z(), xlc, zlc, xyzLocM.X() - locD.X(), xyzLocM.Z() - locD.Z()); + auto gloD = gman->getMatrixL2G(chipID)(locD); // convert to global + + ////// HITS + Hit& hit = (*hitArray[lab.getEventID()])[hitEntry->second]; + // mean local position of the hit + auto locH = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local + auto locHsta = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); + o2::math_utils::Point3D locHmid; + float x0, y0, z0, dltx, dlty, dltz, r; + if (isIB) { + float xFlat{0.}, yFlat{0.}; + mMosaixSegmentations[layer].curvedToFlat(locH.X(), locH.Y(), xFlat, yFlat); + locH.SetCoordinates(xFlat, yFlat, locH.Z()); + mMosaixSegmentations[layer].curvedToFlat(locHsta.X(), locHsta.Y(), xFlat, yFlat); + locHsta.SetCoordinates(xFlat, yFlat, locHsta.Z()); + x0 = locHsta.X(); + dltx = locH.X() - x0; + y0 = locHsta.Y(); + dlty = locH.Y() - y0; + z0 = locHsta.Z(); + dltz = locH.Z() - z0; + r = (o2::its3::constants::pixelarray::pixels::apts::responseYShift - y0) / dlty; + } else { + x0 = locHsta.X(); + dltx = locH.X() - x0; + y0 = locHsta.Y(); + dlty = locH.Y() - y0; + z0 = locHsta.Z(); + dltz = locH.Z() - z0; + r = (0.5 * (o2::itsmft::SegmentationAlpide::SensorLayerThickness - o2::itsmft::SegmentationAlpide::SensorLayerThicknessEff) - y0) / dlty; + } + locHmid.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); + auto gloHmid = gman->getMatrixL2G(chipID) * locHmid; + float theta = std::acos(gloHmid.Z() / gloHmid.Rho()); + float eta = -std::log(std::tan(theta / 2.f)); + + float xlc = 0., zlc = 0.; + int row = 0, col = 0; + + if (isIB) { + float xFlat{0.}, yFlat{0.}; + mMosaixSegmentations[layer].curvedToFlat(locD.X(), locD.Y(), xFlat, yFlat); + locD.SetCoordinates(xFlat, yFlat, locD.Z()); + if (auto v1 = !mMosaixSegmentations[layer].localToDetector(locHmid.X(), locHmid.Z(), row, col), + v2 = !mMosaixSegmentations[layer].detectorToLocal(row, col, xlc, zlc); + v1 || v2) { + continue; + } + } else { + if (auto v1 = !SegmentationAlpide::localToDetector(locHmid.X(), locHmid.Z(), row, col), + v2 = !SegmentationAlpide::detectorToLocal(row, col, xlc, zlc); + v1 || v2) { + continue; + } + } + + nt->Fill(chipID, gloD.X(), gloD.Y(), gloD.Z(), ix, iz, row, col, locHmid.X(), locHmid.Z(), xlc, zlc, locHmid.X() - locD.X(), locHmid.Z() - locD.Z(), eta); - (isIB) ? ++nDigitFilledIB : ++nDigitFilledOB; - } // end loop on digits array - } // end loop on ROFRecords array + (isIB) ? ++nDigitFilledIB : ++nDigitFilledOB; + } // end loop on digits array + } // end loop on ROFRecords array + + Info("EXIT", "read %d filled %d in IB\n", nDigitReadIB, nDigitFilledIB); + Info("EXIT", "read %d filled %d in OB\n", nDigitReadOB, nDigitFilledOB); + } else { + f = TFile::Open("CheckDigits.root", "Open"); + nt = f->Get("ntd"); + } auto canvXY = new TCanvas("canvXY", "", 1600, 1600); canvXY->Divide(2, 2); @@ -234,7 +269,7 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil nt->Draw("y:x>>h_y_vs_x_OB(1000, -50, 50, 1000, -50, 50)", "id >= 3456", "colz"); canvXY->cd(4); nt->Draw("y:z>>h_y_vs_z_OB(1000, -100, 100, 1000, -50, 50)", "id >= 3456", "colz"); - canvXY->SaveAs("it3digits_y_vs_x_vs_z.pdf"); + canvXY->SaveAs("it3digits_y_vs_x_vs_z.png"); auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); canvdXdZ->Divide(2, 2); @@ -258,10 +293,8 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OB_z"); Info("OB |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("OB |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); - canvdXdZ->SaveAs("it3digits_dx_vs_dz.pdf"); + canvdXdZ->SaveAs("it3digits_dx_vs_dz.png"); f->Write(); f->Close(); - Info("EXIT", "read %d filled %d in IB\n", nDigitReadIB, nDigitFilledIB); - Info("EXIT", "read %d filled %d in OB\n", nDigitReadOB, nDigitFilledOB); } diff --git a/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C b/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C index 459e3c59d1e82..c02b4bc238955 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C @@ -33,6 +33,7 @@ #define ENABLE_UPGRADES #include "DetectorsCommonDataFormats/DetID.h" #include "ITSBase/GeometryTGeo.h" +#include "ITS3Base/SpecsV2.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITS3Base/SegmentationMosaix.h" #include "DataFormatsITSMFT/CompCluster.h" @@ -94,7 +95,7 @@ void CreateDictionariesITS3(bool saveDeltas = true, TNtuple* nt = nullptr; if (saveDeltas) { fout = TFile::Open("CreateDictionaries.root", "recreate"); - nt = new TNtuple("nt", "hashes ntuple", "hash:layer:chipID:xhf:zhf:xcf:zcf:dx:dz:outlimDx:outlimDz"); + nt = new TNtuple("nt", "hashes ntuple", "hash:layer:chipID:xhf:zhf:xcf:zcf:dx:dz:outlimDx:outlimDz:clusterSize:eta"); } const o2::steer::DigitizationContext* digContext = nullptr; @@ -270,16 +271,34 @@ void CreateDictionariesITS3(bool saveDeltas = true, auto xyzLocE = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local auto xyzLocS = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); o2::math_utils::Vector3D xyzLocM; - xyzLocM.SetCoordinates(0.5f * (xyzLocE.X() + xyzLocS.X()), 0.5f * (xyzLocE.Y() + xyzLocS.Y()), 0.5f * (xyzLocE.Z() + xyzLocS.Z())); auto locC = o2::its3::TopologyDictionary::getClusterCoordinates(cluster, pattern, false); int layer = gman->getLayer(chipID); + float x0, y0, z0, dltx, dlty, dltz, r; if (ib) { float xFlat{0.}, yFlat{0.}; - mMosaixSegmentations[layer].curvedToFlat(xyzLocM.X(), xyzLocM.Y(), xFlat, yFlat); - xyzLocM.SetCoordinates(xFlat, yFlat, xyzLocM.Z()); mMosaixSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlat, yFlat); locC.SetCoordinates(xFlat, yFlat, locC.Z()); + mMosaixSegmentations[layer].curvedToFlat(xyzLocE.X(), xyzLocE.Y(), xFlat, yFlat); + xyzLocE.SetCoordinates(xFlat, yFlat, xyzLocE.Z()); + mMosaixSegmentations[layer].curvedToFlat(xyzLocS.X(), xyzLocS.Y(), xFlat, yFlat); + xyzLocS.SetCoordinates(xFlat, yFlat, xyzLocS.Z()); + x0 = xyzLocS.X(); + dltx = xyzLocE.X() - x0; + y0 = xyzLocS.Y(); + dlty = xyzLocE.Y() - y0; + z0 = xyzLocS.Z(); + dltz = xyzLocE.Z() - z0; + r = (o2::its3::constants::pixelarray::pixels::apts::responseYShift - y0) / dlty; + } else { + x0 = xyzLocS.X(); + dltx = xyzLocE.X() - x0; + y0 = xyzLocS.Y(); + dlty = xyzLocE.Y() - y0; + z0 = xyzLocS.Z(); + dltz = xyzLocE.Z() - z0; + r = (0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff) - y0) / dlty; } + xyzLocM.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); auto pitchX = (ib) ? o2::its3::SegmentationMosaix::PitchRow : o2::itsmft::SegmentationAlpide::PitchRow; auto pitchZ = (ib) ? o2::its3::SegmentationMosaix::PitchCol : o2::itsmft::SegmentationAlpide::PitchCol; @@ -302,7 +321,13 @@ void CreateDictionariesITS3(bool saveDeltas = true, } } if (saveDeltas) { - nt->Fill(topology.getHash(), layer, chipID, xyzLocM.X(), xyzLocM.Z(), locC.X(), locC.Z(), dX, dZ, outLimitDx, outLimitDz); + auto vectDiff = xyzLocE - xyzLocS; + auto theta = std::acos(vectDiff.Z() / std::hypot(vectDiff.X(), vectDiff.Y(), vectDiff.Z())); + auto eta = -std::log(std::tan(theta / 2)); + if (ib) { + LOGP(info, "Yhit flat start: {}, end: {}, middle: {}", xyzLocS.Y(), xyzLocE.Y(), xyzLocM.Y()); + } + nt->Fill(topology.getHash(), layer, chipID, xyzLocM.X(), xyzLocM.Z(), locC.X(), locC.Z(), dX, dZ, outLimitDx, outLimitDz, pattern.getNPixels(), eta); } } } else { diff --git a/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C b/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C index bfa2f3bede70d..fa4f7e3910b99 100644 --- a/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C +++ b/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C @@ -18,6 +18,7 @@ #include "DetectorsBase/GeometryManager.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" +#include "ITS3Base/SpecsV2.h" #include "CommonUtils/NameConf.h" #include #include @@ -30,8 +31,6 @@ o2::base::MatLayerCylSet mbLUT; bool testMBLUT(const std::string& lutFile = "matbud.root"); -bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomName = ""); - struct LrData { float rMin = 0.f; float rMax = 0.f; @@ -45,14 +44,17 @@ struct LrData { std::vector lrData; void configLayers(); -bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std::string& geomNameInput) +bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomNamePrefix = "o2sim", const std::string& opts = "") { - auto geomName = o2::base::NameConf::getGeomFileName(geomNameInput); + auto geomName = o2::base::NameConf::getGeomFileName(geomNamePrefix); if (gSystem->AccessPathName(geomName.c_str())) { // if needed, create geometry - std::cout << geomName << " does not exist. Will create it\n"; - gSystem->Exec("$O2_ROOT/bin/o2-sim -n 0"); + std::cout << geomName << " does not exist. Will create it on the fly\n"; + std::stringstream str; + // constructing an **unaligned** geom (Geant3 used since faster initialization) --> can be avoided by passing an existing geometry + str << "${O2_ROOT}/bin/o2-sim-serial -n 0 -e TGeant3 --detectorList ALICE2.1 --configKeyValues \"" << opts << "\" --field 0 -o " << geomNamePrefix; + gSystem->Exec(str.str().c_str()); } - o2::base::GeometryManager::loadGeometry(geomNameInput); + o2::base::GeometryManager::loadGeometry(geomNamePrefix); configLayers(); if (maxLr < 1) { @@ -62,7 +64,7 @@ bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std:: } for (int i = 0; i < maxLr; i++) { auto& l = lrData[i]; - printf("L:%3d %6.2f247 rphiBin = 2.; zBin = 3.; - lrData.emplace_back(LrData(lrData.back().rMax, 258., zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, 258., zSpanH, zBin, rphiBin); zSpanH = 247.f; // ignore large lumps of material at |z|>247 rphiBin = 2.; zBin = 999.; // no segmentation in Z - lrData.emplace_back(LrData(lrData.back().rMax, 280., zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, 280., zSpanH, zBin, rphiBin); // TRD @@ -376,7 +380,7 @@ void configLayers() do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin); } while (lrData.back().rMax < 370); // TOF @@ -387,7 +391,7 @@ void configLayers() do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin); } while (lrData.back().rMax < 400); // rest @@ -398,7 +402,7 @@ void configLayers() zSpanH = lrData.back().rMax; auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin); } while (lrData.back().rMax < 500); } diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h deleted file mode 100644 index e9da619c0efbf..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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_ITS3_FASTMULTEST_ -#define ALICEO2_ITS3_FASTMULTEST_ - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITS3/CompCluster.h" -#include -#include "ITS3Reconstruction/FastMultEstConfig.h" -#include -#include - -namespace o2 -{ -namespace its3 -{ - -struct FastMultEst { - - static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; - - float mult = 0.; /// estimated signal clusters multipliciy at reference (1st?) layer - float noisePerChip = 0.; /// estimated or imposed noise per chip - float cov[3] = {0.}; /// covariance matrix of estimation - float chi2 = 0.; /// chi2 - int nLayersUsed = 0; /// number of layers actually used - uint32_t lastRandomSeed = 0; /// state of the gRandom before - - std::array nClPerLayer{0}; // measured N Cl per layer selectROFs - FastMultEst(); - - static uint32_t getCurrentRandomSeed(); - int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); - - void fillNClPerLayer(const gsl::span& clusters); - float process(const std::array ncl) - { - return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(ncl) : processNoiseFree(ncl); - } - float processNoiseFree(const std::array ncl); - float processNoiseImposed(const std::array ncl); - float process(const gsl::span& clusters) - { - fillNClPerLayer(clusters); - return process(nClPerLayer); - } - static bool sSeedSet; - - ClassDefNV(FastMultEst, 1); -}; - -} // namespace its3 -} // namespace o2 - -#endif diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h deleted file mode 100644 index 1857176b19f1f..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FastMultEstConfig.h -/// \brief Configuration parameters for ITS fast multiplicity estimator -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_ITS_FASTMULTESTCONF_H_ -#define ALICEO2_ITS_FASTMULTESTCONF_H_ - -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" - -namespace o2 -{ -namespace its -{ -struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { - static constexpr int NLayers = 7; // FIXME - - /// acceptance correction per layer (relative to 1st one) - float accCorr[NLayers] = {1.f, 0.895, 0.825, 0.803, 0.720, 0.962, 0.911}; - int firstLayer = 3; /// 1st layer to account - int lastLayer = 6; /// last layer to account - float imposeNoisePerChip = 1.e-7 * 1024 * 512; // assumed noise, free parameter if<0 - - // cuts to reject to low or too high mult events - float cutMultClusLow = 0; /// reject ROF with estimated cluster mult. below this value (no cut if <0) - float cutMultClusHigh = -1; /// reject ROF with estimated cluster mult. above this value (no cut if <0) - float cutMultVtxLow = -1; /// reject seed vertex if its multiplicity below this value (no cut if <0) - float cutMultVtxHigh = -1; /// reject seed vertex if its multiplicity above this value (no cut if <0) - float cutRandomFraction = -1.; /// apply random cut rejecting requested fraction - int randomSeed = 0; /// 0 - do not seet seed, >0 : set as is, <0 : use current time - bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers - - bool isMultCutRequested() const { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; - bool isVtxMultCutRequested() const { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; - bool isPassingRandomRejection() const; - bool isPassingMultCut(float mult) const { return mult >= cutMultClusLow && (mult <= cutMultClusHigh || cutMultClusHigh <= 0.f); } - bool isPassingVtxMultCut(int mult) const { return mult >= cutMultVtxLow && (mult <= cutMultVtxHigh || cutMultVtxHigh <= 0.f); } - - O2ParamDef(FastMultEstConfig, "fastMultConfig"); -}; - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h index 771b13539b759..fa15e73118524 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h @@ -72,7 +72,7 @@ void convertCompactClusters(gsl::span clusters, const its3::TopologyDictionary* dict); int loadROFrameDataITS3(its::TimeFrame<7>* tf, - gsl::span rofs, + gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const its3::TopologyDictionary* dict, diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h index ab2ff0086200b..931628f2cf876 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h @@ -28,7 +28,7 @@ class ITS3TrackingInterface final : public its::ITSTrackingInterface void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj) final; protected: - void loadROF(gsl::span& trackROFspan, + void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels) final; diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx deleted file mode 100644 index fa2ce319328b5..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITS3Reconstruction/FastMultEst.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "Framework/Logger.h" -#include -#include -#include - -using namespace o2::its; - -bool FastMultEst::sSeedSet = false; - -///______________________________________________________ -FastMultEst::FastMultEst() -{ - if (!sSeedSet && FastMultEstConfig::Instance().cutRandomFraction > 0.f) { - sSeedSet = true; - if (FastMultEstConfig::Instance().randomSeed > 0) { - gRandom->SetSeed(FastMultEstConfig::Instance().randomSeed); - } else if (FastMultEstConfig::Instance().randomSeed < 0) { - gRandom->SetSeed(std::time(nullptr) % 0xffff); - } - } -} - -///______________________________________________________ -/// find multiplicity for given set of clusters -void FastMultEst::fillNClPerLayer(const gsl::span& clusters) -{ - int lr = FastMultEst::NLayers - 1, nchAcc = o2::itsmft::ChipMappingITS::getNChips() - o2::itsmft::ChipMappingITS::getNChipsPerLr(lr); - std::memset(&nClPerLayer[0], 0, sizeof(int) * FastMultEst::NLayers); - for (int i = clusters.size(); i--;) { // profit from clusters being ordered in chip increasing order - while (clusters[i].getSensorID() < nchAcc) { - assert(lr >= 0); - nchAcc -= o2::itsmft::ChipMappingITS::getNChipsPerLr(--lr); - } - nClPerLayer[lr]++; - } -} - -///______________________________________________________ -/// find multiplicity for given number of clusters per layer -float FastMultEst::processNoiseFree(const std::array ncl) -{ - // we assume that on the used layers the observed number of clusters is defined by the - // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*mAccCorr - const auto& conf = FastMultEstConfig::Instance(); - - float mat[3] = {0}, b[2] = {0}; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - int nch = o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - float err2i = 1. / ncl[il]; - float m2n = nch * err2i; - mat[0] += err2i * conf.accCorr[il] * conf.accCorr[il]; - mat[2] += nch * m2n; - mat[1] += conf.accCorr[il] * m2n; // non-diagonal element - b[0] += conf.accCorr[il]; - b[1] += nch; - nLayersUsed++; - } - } - mult = noisePerChip = chi2 = -1; - float det = mat[0] * mat[2] - mat[1] * mat[1]; - if (nLayersUsed < 2 || std::abs(det) < 1e-15) { - return -1; - } - float detI = 1. / det; - mult = detI * (b[0] * mat[2] - b[1] * mat[1]); - noisePerChip = detI * (b[1] * mat[0] - b[0] * mat[1]); - cov[0] = mat[2] * detI; - cov[2] = mat[0] * detI; - cov[1] = -mat[1] * detI; - chi2 = 0.; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - int nch = o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - float diff = mult * conf.accCorr[il] + nch * noisePerChip - ncl[il]; - chi2 += diff * diff / ncl[il]; - } - } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; -} - -///______________________________________________________ -/// find multiplicity for given number of clusters per layer with mean noise imposed -float FastMultEst::processNoiseImposed(const std::array ncl) -{ - // we assume that on the used layers the observed number of clusters is defined by the - // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*conf.accCorr - // - // minimize the form sum_lr (noise_i - mu nchips_i)^2 / (mu nchips_i) + lambda_i * (noise_i + mult*acc_i - ncl_i) - // whith noise_i being estimate of the noise clusters in nchips_i of layer i, mu is the mean noise per chip, - // mult is the number of signal clusters on the ref. (1st) layer and the acc_i is the acceptance of layer i wrt 1st. - // The lambda_i is hust a Lagrange multiplier. - - const auto& conf = FastMultEstConfig::Instance(); - float w2sum = 0., wnsum = 0., wsum = 0.; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float nchInv = 1. / o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - w2sum += conf.accCorr[il] * conf.accCorr[il] * nchInv; - wnsum += ncl[il] * nchInv * conf.accCorr[il]; - wsum += conf.accCorr[il]; - nLayersUsed++; - } - } - mult = 0; - chi2 = -1; - noisePerChip = conf.imposeNoisePerChip; - if (nLayersUsed < 1) { - return -1; - } - auto w2sumI = 1. / w2sum; - mult = (wnsum - noisePerChip * wsum) * w2sumI; - cov[0] = wnsum * w2sumI; - cov[2] = 0.; - cov[1] = 0.; - - chi2 = 0.; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float noise = ncl[il] - mult * conf.accCorr[il], estNoise = o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * noisePerChip; - float diff = noise - estNoise; - chi2 += diff * diff / estNoise; - } - } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; -} - -int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) -{ - int nrof = rofs.size(), nsel = 0; - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - sel.clear(); - sel.resize(nrof, true); // by default select all - lastRandomSeed = gRandom->GetSeed(); - if (multEstConf.isMultCutRequested()) { - for (uint32_t irof = 0; irof < nrof; irof++) { - nsel += sel[irof] = multEstConf.isPassingMultCut(process(rofs[irof].getROFData(clus))); - } - } else { - nsel = nrof; - } - using IdNT = std::pair; - if (multEstConf.cutRandomFraction > 0.) { - int ntrig = trig.size(), currTrig = 0; - if (multEstConf.preferTriggered) { - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - std::vector nTrigROF; - nTrigROF.reserve(nrof); - for (uint32_t irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - if (nsel && gRandom->Rndm() < multEstConf.cutRandomFraction) { - nsel--; - } - auto irROF = rofs[irof].getBCData(); - while (currTrig < ntrig && trig[currTrig].ir < irROF) { // triggers are sorted, jump to 1st one not less than current ROF - currTrig++; - } - auto& trof = nTrigROF.emplace_back(irof, 0); - irROF += alpParams.roFrameLengthInBC; - while (currTrig < ntrig && trig[currTrig].ir < irROF) { - trof.second++; - currTrig++; - } - } - } - if (nsel > 0) { - sort(nTrigROF.begin(), nTrigROF.end(), [](const IdNT& a, const IdNT& b) { return a.second > b.second; }); // order in number of triggers - auto last = nTrigROF.begin() + nsel; - sort(nTrigROF.begin(), last, [](const IdNT& a, const IdNT& b) { return a.first < b.first; }); // order in ROF ID first nsel ROFs - } - for (int i = nsel; i < int(nTrigROF.size()); i++) { // reject ROFs in the tail - sel[nTrigROF[i].first] = false; - } - } else { // dummy random rejection - for (int irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - float sr = gRandom->Rndm(); - if (gRandom->Rndm() < multEstConf.cutRandomFraction) { - sel[irof] = false; - nsel--; - } - } - } - } - } - LOGP(debug, "NSel = {} of {} rofs Seeds: before {} after {}", nsel, nrof, lastRandomSeed, gRandom->GetSeed()); - - return nsel; -} diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index a01eb77af1677..d7ba4d48dbce4 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -33,6 +33,8 @@ void convertCompactClusters(gsl::span clusters, const its3::TopologyDictionary* dict) { auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + bool applyMisalignment = false; const auto& conf = o2::its::TrackerParamConfig::Instance(); for (int il = 0; il < geom->getNumberOfLayers(); ++il) { @@ -46,8 +48,7 @@ void convertCompactClusters(gsl::span clusters, float sigmaY2, sigmaZ2, sigmaYZ = 0; auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2); const auto detID = c.getSensorID(); - auto& cl3d = output.emplace_back(detID, - (its3::constants::detID::isDetITS3(detID) ? geom->getT2LMatrixITS3(detID, geom->getSensorRefAlpha(detID)) : geom->getMatrixT2L(detID)) ^ locXYZ); // local --> tracking + auto& cl3d = output.emplace_back(detID, geom->getMatrixT2L(detID) ^ locXYZ); // local --> tracking if (applyMisalignment) { auto lrID = geom->getLayer(detID); sigmaY2 += conf.sysErrY2[lrID]; @@ -58,7 +59,7 @@ void convertCompactClusters(gsl::span clusters, } int loadROFrameDataITS3(its::TimeFrame<7>* tf, - gsl::span rofs, + gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const its3::TopologyDictionary* dict, @@ -67,15 +68,16 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, auto geom = its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - tf->mNrof = 0; + tf->resetROFrameData(rofs.size()); + tf->prepareROFrameData(rofs, clusters); its::bounded_vector clusterSizeVec(clusters.size(), tf->getMemoryPool().get()); - for (auto& rof : rofs) { + for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { + const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { auto& c = clusters[clusterId]; auto sensorID = c.getSensorID(); - auto isITS3 = its3::constants::detID::isDetITS3(sensorID); auto layer = geom->getLayer(sensorID); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; @@ -86,16 +88,11 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, // Transformation to the local --> global auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - // for cylindrical layers we have a different alpha for each cluster, for regular silicon detectors instead a single alpha for the whole sensor + // Inverse transformation to the local --> tracking + o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; + + // Tracking alpha angle float alpha = geom->getSensorRefAlpha(sensorID); - o2::math_utils::Point3D trkXYZ; - if (isITS3) { - // Inverse transformation to the local --> tracking - trkXYZ = geom->getT2LMatrixITS3(sensorID, alpha) ^ locXYZ; - } else { - // Inverse transformation to the local --> tracking - trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; - } tf->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, std::array{trkXYZ.y(), trkXYZ.z()}, @@ -106,9 +103,8 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, tf->addClusterExternalIndexToLayer(layer, clusterId); } for (unsigned int iL{0}; iL < tf->getUnsortedClusters().size(); ++iL) { - tf->mROFramesClusters[iL].push_back(tf->getUnsortedClusters()[iL].size()); + tf->mROFramesClusters[iL][iRof + 1] = tf->getUnsortedClusters()[iL].size(); } - tf->mNrof++; } tf->setClusterSize(clusterSizeVec); diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx index 61ab051ffb565..0d1deb77b7c2e 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx @@ -13,6 +13,7 @@ #include "ITS3Reconstruction/TopologyDictionary.h" #include "ITS3Base/SegmentationMosaix.h" +#include "ITS3Base/SpecsV2.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "CommonUtils/StringUtils.h" #include @@ -202,6 +203,7 @@ math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::C auto layer = its3::constants::detID::getDetID2Layer(cl.getSensorID()); mIBSegmentations[layer].detectorToLocalUnchecked(cl.getRow(), cl.getCol(), locCl); locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID(), true) * its3::SegmentationMosaix::PitchRow); + locCl.SetY(its3::constants::pixelarray::pixels::apts::responseYShift); locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID(), true) * its3::SegmentationMosaix::PitchCol); float xCurved{0.f}, yCurved{0.f}; mIBSegmentations[layer].flatToCurved(locCl.X(), locCl.Y(), xCurved, yCurved); diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx index afb276e956e76..0f5c66a7f9663 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx @@ -12,6 +12,7 @@ #include "ITS3Reconstruction/TrackingInterface.h" #include "ITS3Reconstruction/IOUtils.h" #include "ITSBase/GeometryTGeo.h" +#include "ITStracking/TrackingConfigParam.h" #include "ITSMFTBase/DPLAlpideParam.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DeviceSpec.h" @@ -33,7 +34,6 @@ void ITS3TrackingInterface::updateTimeDependentParams(framework::ProcessingConte auto geom = its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); initialise(); - getConfiguration(pc); if (pc.services().get().inputTimesliceId == 0) { // print settings only for the 1st pipeling o2::its::VertexerParamConfig::Instance().printKeyValues(); o2::its::TrackerParamConfig::Instance().printKeyValues(); @@ -74,7 +74,7 @@ void ITS3TrackingInterface::finaliseCCDB(framework::ConcreteDataMatcher& matcher } } -void ITS3TrackingInterface::loadROF(gsl::span& trackROFspan, +void ITS3TrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels) diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h index d1b54f81face4..80565df55d154 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h @@ -45,9 +45,10 @@ class DescriptorInnerBarrelITS3 : public o2::its::DescriptorInnerBarrel int mNumLayers{constants::nLayers}; // wrapper volume properties - static constexpr double mWrapperMinRadiusITS3{1.8}; - static constexpr double mWrapperMaxRadiusITS3{4.}; - static constexpr double mWrapperZSpanITS3{constants::segment::length + 5.}; + static constexpr double mTolerance{1e-3}; + static constexpr double mWrapperMinRadiusITS3{constants::radiiInner[0] - mTolerance}; + static constexpr double mWrapperMaxRadiusITS3{constants::services::radiusOuter + mTolerance}; + static constexpr double mWrapperZSpanITS3{constants::services::length * 2 + mTolerance}; // z length is divided in half private: std::array, constants::nLayers> mIBLayers; diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.h index 5764dfbd7d593..e3a2a5d0d0efb 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.h @@ -48,16 +48,17 @@ class DigiParams final : public o2::itsmft::DigiParams const o2::itsmft::AlpideSimResponse* getOBSimResponse() const { return mOBSimResponse; } void setOBSimResponse(const o2::itsmft::AlpideSimResponse* response) { mOBSimResponse = response; } - o2::its3::ChipSimResponse* getIBSimResponse() const { return mIBSimResponse; } - void setIBSimResponse(o2::its3::ChipSimResponse* response); + o2::its3::ChipSimResponse* getIBSimResponse() const { return mIBSimResponse.get(); } + void setIBSimResponse(const o2::itsmft::AlpideSimResponse* resp); bool hasResponseFunctions() const { return mIBSimResponse != nullptr && mOBSimResponse != nullptr; } void print() const final; private: - const o2::itsmft::AlpideSimResponse* mOBSimResponse = nullptr; //!< pointer to external response - o2::its3::ChipSimResponse* mIBSimResponse = nullptr; //!< pointer to external response + const o2::itsmft::AlpideSimResponse* mOBSimResponse = nullptr; //!< pointer to external response + const o2::itsmft::AlpideSimResponse* mIBSimResponseExt = nullptr; //!< pointer to external response + std::unique_ptr mIBSimResponse = nullptr; //!< pointer to external response ClassDef(DigiParams, 1); }; diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h index a2dd1102091da..866973083983b 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h @@ -42,7 +42,7 @@ class Digitizer : public TObject using ExtraDig = std::vector; ///< container for extra contributions to PreDigits public: - ~Digitizer(); + ~Digitizer() = default; void setDigits(std::vector* dig) { mDigits = dig; } void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } @@ -111,18 +111,18 @@ class Digitizer : public TObject static constexpr std::array mIBSegmentations{0, 1, 2}; - o2::its3::ChipSimResponse* mSimRespIB = nullptr; // simulated response for IB - o2::itsmft::AlpideSimResponse* mSimRespOB = nullptr; // simulated response for OB - bool mSimRespIBOrientation{false}; // wether the orientation in the IB response function is flipped - float mSimRespIBShift{0.f}; // adjusting the Y-shift in the IB response function to match sensor local coord. - float mSimRespIBScaleX{1.f}; // scale x-local coordinate to response function x-coordinate - float mSimRespIBScaleZ{1.f}; // scale z-local coordinate to response function z-coordinate - float mSimRespOBShift{0.f}; // adjusting the Y-shift in the OB response function to match sensor local coord. + const o2::its3::ChipSimResponse* mSimRespIB = nullptr; // simulated response for IB + const o2::itsmft::AlpideSimResponse* mSimRespOB = nullptr; // simulated response for OB + bool mSimRespIBOrientation{false}; // wether the orientation in the IB response function is flipped + float mSimRespIBShift{0.f}; // adjusting the Y-shift in the IB response function to match sensor local coord. + float mSimRespIBScaleX{1.f}; // scale x-local coordinate to response function x-coordinate + float mSimRespIBScaleZ{1.f}; // scale z-local coordinate to response function z-coordinate + float mSimRespOBShift{0.f}; // adjusting the Y-shift in the OB response function to match sensor local coord. const o2::its::GeometryTGeo* mGeometry = nullptr; ///< ITS3 geometry - std::vector mChips; ///< Array of chips digits containers - std::deque> mExtraBuff; ///< burrer (per roFrame) for extra digits + std::vector mChips; ///< Array of chips digits containers + std::deque> mExtraBuff; ///< burrer (per roFrame) for extra digits std::vector* mDigits = nullptr; //! output digits std::vector* mROFRecords = nullptr; //! output ROF records diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h index fd9195f9ee228..f45a4469ae2b8 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h @@ -26,7 +26,7 @@ namespace o2::its3 { /// This class defines the geometry for the ITS3 IB layers. -class ITS3Layer +class ITS3Layer final { // The hierarchy will be the following: // ITS2 -> ITS3 @@ -76,7 +76,6 @@ class ITS3Layer void buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat = nullptr, BuildLevel level = BuildLevel::kAll, bool createMaterials = false); private: - bool mBuilt{false}; TGeoMedium* mSilicon{nullptr}; TGeoMedium* mAir{nullptr}; TGeoMedium* mCarbon{nullptr}; @@ -91,7 +90,7 @@ class ITS3Layer void createSegment(); void createChip(); void createCarbonForm(); - TGeoCompositeShape* getHringShape(TGeoTubeSeg* Hring); + TGeoCompositeShape* getHringShape(TGeoTubeSeg* Hring) const; void createLayerImpl(); uint8_t mNLayer{0}; // Layer number diff --git a/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx index 102b15863683e..efe878536687d 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx @@ -31,12 +31,12 @@ void ChipDigitsContainer::addNoise(UInt_t rofMin, UInt_t rofMax, const o2::its3: int nel = 0; if (isIB()) { - // Inner barrel: use ITS3-specific noise interface with OB segmentation. - mean = params->getIBNoisePerPixel() * SegmentationOB::NPixels; + // Inner barrel: use ITS3-specific noise interface with IB segmentation. + mean = params->getIBNoisePerPixel() * SegmentationIB::NPixels; nel = static_cast(params->getIBChargeThreshold() * 1.1); } else { - // Outer barrel: use base class noise interface with IB segmentation. - mean = params->getNoisePerPixel() * SegmentationIB::NPixels; + // Outer barrel: use base class noise interface with OB segmentation. + mean = params->getNoisePerPixel() * SegmentationOB::NPixels; nel = static_cast(params->getChargeThreshold() * 1.1); } diff --git a/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx b/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx index 1c482983f0d0a..72b291fb0d653 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx @@ -25,38 +25,66 @@ void ChipSimResponse::initData(int tableNumber, std::string dataPath, const bool void ChipSimResponse::computeCentreFromData() { - std::vector zVec, qVec; const int npix = o2::itsmft::AlpideRespSimMat::getNPix(); + std::vector zVec, effVec; + zVec.reserve(mNBinDpt); + effVec.reserve(mNBinDpt); for (int iz = 0; iz < mNBinDpt; ++iz) { - size_t bin = iz + mNBinDpt * (0 + mNBinRow * 0); - const auto& mat = mData[bin]; - float val = mat.getValue(npix / 2, npix / 2); - float gz = mDptMin + iz / mStepInvDpt; - zVec.push_back(gz); - qVec.push_back(val); + int rev = mNBinDpt - 1 - iz; + float z = mDptMin + iz / mStepInvDpt; + float sum = 0.f; + const auto& mat = mData[rev]; + for (int ix = 0; ix < npix; ++ix) { + for (int iy = 0; iy < npix; ++iy) { + sum += mat.getValue(ix, iy); + } + } + zVec.push_back(z); + effVec.push_back(sum); } - std::vector> zqPairs; - for (size_t i = 0; i < zVec.size(); ++i) { - zqPairs.emplace_back(zVec[i], qVec[i]); - } - std::sort(zqPairs.begin(), zqPairs.end()); - zVec.clear(); - qVec.clear(); - for (auto& p : zqPairs) { - zVec.push_back(p.first); - qVec.push_back(p.second); - } + struct Bin { + float z0, z1, q0, q1, dq; + }; + std::vector bins; + bins.reserve(zVec.size() - 1); - float intQ = 0.f, intZQ = 0.f; + float totQ = 0.f; for (size_t i = 0; i + 1 < zVec.size(); ++i) { float z0 = zVec[i], z1 = zVec[i + 1]; - float q0 = qVec[i], q1 = qVec[i + 1]; - float dz = z1 - z0; - intQ += 0.5f * (q0 + q1) * dz; - intZQ += 0.5f * (z0 * q0 + z1 * q1) * dz; + float q0 = effVec[i], q1 = effVec[i + 1]; + float dq = 0.5f * (q0 + q1) * (z1 - z0); + bins.push_back({z0, z1, q0, q1, dq}); + totQ += dq; + } + + if (totQ <= 0.f) { + mRespCentreDep = mDptMin; + return; + } + + float halfQ = 0.5f * totQ; + float cumQ = 0.f; + for (auto& b : bins) { + if (cumQ + b.dq < halfQ) { + cumQ += b.dq; + continue; + } + float dz = b.z1 - b.z0; + float slope = (b.q1 - b.q0) / dz; + float disc = b.q0 * b.q0 - 2.f * slope * (cumQ - halfQ); + + float x; + if (disc >= 0.f && std::abs(slope) > 1e-6f) { + x = (-b.q0 + std::sqrt(disc)) / slope; + } else { + x = (halfQ - cumQ) / b.q0; + } + x = std::clamp(x, 0.f, dz); + mRespCentreDep = b.z0 + x; + return; } - mRespCentreDep = (intQ > 0.f) ? intZQ / intQ : 0.f; + mRespCentreDep = mDptMax; } diff --git a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx index 540e1d41f1c62..04f244284d5b6 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" -#include "fairlogger/Logger.h" using namespace o2::its3; @@ -18,14 +17,12 @@ ClassImp(DescriptorInnerBarrelITS3); void DescriptorInnerBarrelITS3::createLayer(int iLayer, TGeoVolume* dest) { - LOGP(debug, "ITS3-IB: Creating Layer {}", iLayer); mIBLayers[iLayer] = std::make_unique(iLayer); mIBLayers[iLayer]->createLayer(dest); } void DescriptorInnerBarrelITS3::createServices(TGeoVolume* dest) { - LOGP(debug, "ITS3-IB: Creating Services"); mServices = std::make_unique(); mServices->createCYSSAssembly(dest); } diff --git a/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx index afa02ec44741d..e5923d0bb7a1e 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx @@ -69,12 +69,14 @@ void DigiParams::print() const getSignalShape().print(); } -void DigiParams::setIBSimResponse(o2::its3::ChipSimResponse* response) +void DigiParams::setIBSimResponse(const o2::itsmft::AlpideSimResponse* resp) { - mIBSimResponse = response; - if (mIBSimResponse) { - mIBSimResponse->computeCentreFromData(); + if (!resp) { + LOGP(fatal, "cannot set response from nullptr"); } + mIBSimResponseExt = resp; + mIBSimResponse = std::make_unique(mIBSimResponseExt); + mIBSimResponse->computeCentreFromData(); } } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx index b91e17890a6d8..4b0374d925401 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx @@ -35,11 +35,6 @@ using o2::itsmft::PreDigit; using namespace o2::its3; -Digitizer::~Digitizer() -{ - delete mSimRespIB; -} - void Digitizer::init() { const int numOfChips = mGeometry->getNumberOfChips(); @@ -53,46 +48,22 @@ void Digitizer::init() } if (!mParams.hasResponseFunctions()) { - auto loadSetResponseFunc = [&](const char* fileIB, const char* nameIB, const char* fileOB, const char* nameOB) { - LOGP(info, "Loading response function IB={}:{} ; OB={}:{}", nameIB, fileIB, nameOB, fileOB); - auto fIB = TFile::Open(fileIB, "READ"); - if (!fIB || fIB->IsZombie() || !fIB->IsOpen()) { - LOGP(fatal, "Cannot open file {}", fileIB); - } - auto fOB = TFile::Open(fileOB, "READ"); - if (!fOB || fOB->IsZombie() || !fOB->IsOpen()) { - LOGP(fatal, "Cannot open file {}", fileOB); - } - if ((mSimRespIB = new o2::its3::ChipSimResponse(fIB->Get(nameIB))) == nullptr) { - LOGP(fatal, "Cannot create response function for IB"); - } - if ((mSimRespOB = fOB->Get(nameOB)) == nullptr) { - LOGP(fatal, "Cannot create response function for OB"); - } - mParams.setIBSimResponse(mSimRespIB); - mParams.setOBSimResponse(mSimRespOB); - fIB->Close(); - fOB->Close(); - }; - - if (const auto& func = ITS3Params::Instance().chipResponseFunction; func == "Alpide") { - constexpr const char* responseFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; - loadSetResponseFunc(responseFile, "response0", responseFile, "response0"); - mSimRespIBScaleX = o2::itsmft::SegmentationAlpide::PitchRow / SegmentationIB::PitchRow; - mSimRespIBScaleZ = o2::itsmft::SegmentationAlpide::PitchCol / SegmentationIB::PitchCol; - } else if (func == "APTS") { - constexpr const char* responseFileIB = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; - constexpr const char* responseFileOB = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; - loadSetResponseFunc(responseFileIB, "response1", responseFileOB, "response0"); - mSimRespIBScaleX = constants::pixelarray::pixels::apts::pitchX / SegmentationIB::PitchRow; - mSimRespIBScaleZ = constants::pixelarray::pixels::apts::pitchZ / SegmentationIB::PitchCol; - mSimRespIBOrientation = true; - } else { - LOGP(fatal, "ResponseFunction '{}' not implemented!", func); - } - mSimRespIBShift = mSimRespIB->getDepthMax() - constants::silicon::thickness / 2.f; - mSimRespOBShift = mSimRespOB->getDepthMax() - SegmentationOB::SensorLayerThickness / 2.f; + LOGP(fatal, "No response functions set!"); + } + if (const auto& func = ITS3Params::Instance().chipResponseFunction; func == "Alpide") { + mSimRespIBScaleX = o2::itsmft::SegmentationAlpide::PitchRow / SegmentationIB::PitchRow; + mSimRespIBScaleZ = o2::itsmft::SegmentationAlpide::PitchCol / SegmentationIB::PitchCol; + } else if (func == "APTS") { + mSimRespIBScaleX = constants::pixelarray::pixels::apts::pitchX / SegmentationIB::PitchRow; + mSimRespIBScaleZ = constants::pixelarray::pixels::apts::pitchZ / SegmentationIB::PitchCol; + mSimRespIBOrientation = true; + } else { + LOGP(fatal, "ResponseFunction '{}' not implemented!", func); } + mSimRespIB = mParams.getIBSimResponse(); + mSimRespOB = mParams.getOBSimResponse(); + mSimRespIBShift = mSimRespIB->getDepthMax() - constants::silicon::thickness / 2.f; + mSimRespOBShift = mSimRespOB->getDepthMax() - SegmentationOB::SensorLayerThickness / 2.f; mParams.print(); LOGP(info, "IB shift = {} ; OB shift = {}", mSimRespIBShift, mSimRespOBShift); @@ -105,10 +76,10 @@ void Digitizer::process(const std::vector* hits, int evID, int srcI { // digitize single event, the time must have been set beforehand - LOG(info) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " - << srcID << " at time " << mEventTime << " ROFrame = " << mNewROFrame << ")" - << " cont.mode: " << isContinuous() - << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; + LOG(debug) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " + << srcID << " at time " << mEventTime << " ROFrame = " << mNewROFrame << ")" + << " cont.mode: " << isContinuous() + << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; // is there something to flush ? if (mNewROFrame > mROFrameMin) { @@ -234,7 +205,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) { // convert single hit to digits - int chipID = hit.GetDetectorID(); + auto chipID = hit.GetDetectorID(); auto& chip = mChips[chipID]; if (chip.isDisabled()) { return; diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index 8dc94e339c793..c0f8fdc19d03b 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -67,11 +67,11 @@ void ITS3Layer::createLayer(TGeoVolume* motherVolume) // Create one layer of ITS3 and attach it to the motherVolume. getMaterials(); createLayerImpl(); - mBuilt = true; if (motherVolume == nullptr) { return; } + // Add it to motherVolume auto* trans = new TGeoTranslation(0, 0, -constants::segment::lengthSensitive / 2.); motherVolume->AddNode(mLayer, 0, trans); @@ -122,8 +122,8 @@ void ITS3Layer::createTile() mTile->AddNode(mPixelArray, 0, phiRotPixelArray); // Biasing - double biasPhi1 = constants::pixelarray::width / mR * o2m::Rad2Deg + readoutPhi2; - double biasPhi2 = biasing::width / mR * o2m::Rad2Deg + biasPhi1; + double biasPhi1 = (constants::pixelarray::width / mR * o2m::Rad2Deg) + readoutPhi2; + double biasPhi2 = (biasing::width / mR * o2m::Rad2Deg) + biasPhi1; auto biasing = new TGeoTubeSeg(mRmin, mRmax, biasing::length / 2, biasPhi1, biasPhi2); auto biasingVol = new TGeoVolume(Form("biasing%d", mNLayer), biasing, mSilicon); biasingVol->SetLineColor(biasing::color); @@ -131,9 +131,9 @@ void ITS3Layer::createTile() mTile->AddNode(biasingVol, 0); // Power Switches are on the side right side of the pixel array and biasing. - auto zMovePowerSwitches = new TGeoTranslation(0, 0, +powerswitches::length / 2. + constants::pixelarray::length / 2.); + auto zMovePowerSwitches = new TGeoTranslation(0, 0, (+powerswitches::length / 2.) + (constants::pixelarray::length / 2.)); double powerPhi1 = readoutPhi2; - double powerPhi2 = powerswitches::width / mR * o2m::Rad2Deg + powerPhi1; + double powerPhi2 = (powerswitches::width / mR * o2m::Rad2Deg) + powerPhi1; auto powerSwitches = new TGeoTubeSeg(mRmin, mRmax, powerswitches::length / 2, powerPhi1, powerPhi2); auto powerSwitchesVol = new TGeoVolume(Form("powerswitches%d", mNLayer), powerSwitches, mSilicon); powerSwitchesVol->SetLineColor(powerswitches::color); @@ -166,7 +166,7 @@ void ITS3Layer::createRSU() // Lower Left auto zMoveLL1 = new TGeoTranslation(0, 0, constants::tile::length); auto zMoveLL2 = new TGeoTranslation(0, 0, constants::tile::length * 2.); - auto zMoveLLDB = new TGeoTranslation(0, 0, -databackbone::length / 2. - constants::pixelarray::length / 2.); + auto zMoveLLDB = new TGeoTranslation(0, 0, (-databackbone::length / 2.) - (constants::pixelarray::length / 2.)); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, nullptr); mRSU->AddNode(mTile, nCopyRSU++, zMoveLL1); @@ -175,9 +175,9 @@ void ITS3Layer::createRSU() // Lower Right auto zMoveLR0 = new TGeoTranslation(0, 0, +length / 2.); - auto zMoveLR1 = new TGeoTranslation(0, 0, constants::tile::length + length / 2.); - auto zMoveLR2 = new TGeoTranslation(0, 0, constants::tile::length * 2. + length / 2.); - auto zMoveLRDB = new TGeoTranslation(0, 0, -databackbone::length / 2. + length / 2. - constants::pixelarray::length / 2.); + auto zMoveLR1 = new TGeoTranslation(0, 0, constants::tile::length + (length / 2.)); + auto zMoveLR2 = new TGeoTranslation(0, 0, (constants::tile::length * 2.) + (length / 2.)); + auto zMoveLRDB = new TGeoTranslation(0, 0, (-databackbone::length / 2.) + (length / 2.) - (constants::pixelarray::length / 2.)); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, zMoveLR0); mRSU->AddNode(mTile, nCopyRSU++, zMoveLR1); @@ -192,7 +192,7 @@ void ITS3Layer::createRSU() // Upper Left auto zMoveUL1 = new TGeoCombiTrans(0, 0, constants::tile::length, rot); auto zMoveUL2 = new TGeoCombiTrans(0, 0, constants::tile::length * 2., rot); - auto zMoveULDB = new TGeoCombiTrans(0, 0, -databackbone::length / 2. - constants::pixelarray::length / 2., rot); + auto zMoveULDB = new TGeoCombiTrans(0, 0, (-databackbone::length / 2.) - (constants::pixelarray::length / 2.), rot); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, rot); mRSU->AddNode(mTile, nCopyRSU++, zMoveUL1); @@ -201,9 +201,9 @@ void ITS3Layer::createRSU() // Upper Right auto zMoveUR0 = new TGeoCombiTrans(0, 0, +length / 2., rot); - auto zMoveUR1 = new TGeoCombiTrans(0, 0, constants::tile::length + length / 2., rot); - auto zMoveUR2 = new TGeoCombiTrans(0, 0, constants::tile::length * 2. + length / 2., rot); - auto zMoveURDB = new TGeoCombiTrans(0, 0, -databackbone::length / 2. + length / 2. - constants::pixelarray::length / 2., rot); + auto zMoveUR1 = new TGeoCombiTrans(0, 0, constants::tile::length + (length / 2.), rot); + auto zMoveUR2 = new TGeoCombiTrans(0, 0, (constants::tile::length * 2.) + (length / 2.), rot); + auto zMoveURDB = new TGeoCombiTrans(0, 0, (-databackbone::length / 2.) + (length / 2.) - (constants::pixelarray::length / 2.), rot); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, zMoveUR0); mRSU->AddNode(mTile, nCopyRSU++, zMoveUR1); @@ -225,9 +225,9 @@ void ITS3Layer::createSegment() mSegment = new TGeoVolumeAssembly(its3TGeo::getITS3SegmentPattern(mNLayer)); mSegment->VisibleDaughters(); - for (size_t i{0}; i < nRSUs; ++i) { - auto zMove = new TGeoTranslation(0, 0, +i * constants::rsu::length + constants::rsu::databackbone::length + constants::pixelarray::length / 2.); - mSegment->AddNode(mRSU, i, zMove); + for (unsigned int i{0}; i < nRSUs; ++i) { + auto zMove = new TGeoTranslation(0, 0, (i * constants::rsu::length) + constants::rsu::databackbone::length + (constants::pixelarray::length / 2.)); + mSegment->AddNode(mRSU, (int)i, zMove); } // LEC @@ -242,7 +242,7 @@ void ITS3Layer::createSegment() mSegment->AddNode(lecVol, 0, zMoveLEC); // REC; reuses lecPhi1,2 - auto zMoveREC = new TGeoTranslation(0, 0, nRSUs * constants::rsu::length + rec::length / 2.); + auto zMoveREC = new TGeoTranslation(0, 0, (nRSUs * constants::rsu::length) + (rec::length / 2.)); auto rec = new TGeoTubeSeg(mRmin, mRmax, rec::length / 2., lecPhi1, lecPhi2); auto recVol = new TGeoVolume(Form("rec%d", mNLayer), rec, mSilicon); @@ -266,11 +266,11 @@ void ITS3Layer::createChip() auto phiOffset = constants::segment::width / mR * o2m::Rad2Deg; for (unsigned int i{0}; i < constants::nSegments[mNLayer]; ++i) { auto rot = new TGeoRotation(Form("its3PhiSegmentOffset_%d_%d", mNLayer, i), 0, 0, phiOffset * i); - mChip->AddNode(mSegment, i, rot); + mChip->AddNode(mSegment, (int)i, rot); } // Add metal stack positioned radially outward - auto zMoveMetal = new TGeoTranslation(0, 0, constants::metalstack::length / 2. - constants::segment::lec::length); + auto zMoveMetal = new TGeoTranslation(0, 0, (constants::metalstack::length / 2.) - constants::segment::lec::length); auto metal = new TGeoTubeSeg(mRmax, mRmax + constants::metalstack::thickness, constants::metalstack::length / 2., 0, constants::nSegments[mNLayer] * phiOffset); auto metalVol = new TGeoVolume(Form("metal%d", mNLayer), metal, mCopper); metalVol->SetLineColor(constants::metalstack::color); @@ -293,10 +293,10 @@ void ITS3Layer::createCarbonForm() if (mNLayer < 2) { dRadius = constants::radii[mNLayer + 1] - constants::radii[mNLayer] - constants::totalThickness; } else { - dRadius = 0.7; // TODO: lack of carbon foam radius for layer 2, use 0.7mm as a temporary value + dRadius = constants::carbonfoam::thicknessOuterFoam; // TODO: lack of carbon foam radius for layer 2, use 0.7 cm as a temporary value } double phiSta = edgeBetwChipAndFoam / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; - double phiEnd = (constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg - phiSta; + double phiEnd = ((constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg) - phiSta; double phiLongeronsCover = longeronsWidth / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; // H-rings foam @@ -308,35 +308,37 @@ void ITS3Layer::createCarbonForm() HringCVol->SetLineColor(color); auto HringAVol = new TGeoVolume(Form("hringA%d", mNLayer), HringAWithHoles, mCarbon); HringAVol->SetLineColor(color); - auto zMoveHringC = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2.); - auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2. + constants::segment::length - HringLength); + auto zMoveHringC = new TGeoTranslation(0, 0, -constants::segment::lec::length + (HringLength / 2.)); + auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + (HringLength / 2.) + constants::segment::length - HringLength); // Longerons are made by same material - [[maybe_unused]] auto longeronR = new TGeoTubeSeg(Form("longeronR%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2, phiSta, phiSta + phiLongeronsCover); - [[maybe_unused]] auto longeronL = new TGeoTubeSeg(Form("longeronL%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2, phiEnd - phiLongeronsCover, phiEnd); - TString nameLongerons = Form("longeronR%d + longeronL%d", mNLayer, mNLayer); - auto longerons = new TGeoCompositeShape(nameLongerons); - auto longeronsVol = new TGeoVolume(Form("longerons%d", mNLayer), longerons, mCarbon); - longeronsVol->SetLineColor(color); - auto zMoveLongerons = new TGeoTranslation(0, 0, -constants::segment::lec::length + constants::segment::length / 2.); + // added separately to make navigation faster + [[maybe_unused]] auto longeronR = new TGeoTubeSeg(Form("longeronR%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiSta, phiSta + phiLongeronsCover); + [[maybe_unused]] auto longeronL = new TGeoTubeSeg(Form("longeronL%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiEnd - phiLongeronsCover, phiEnd); + auto longeronRVol = new TGeoVolume(Form("longeronR%d", mNLayer), longeronR, mCarbon); + longeronRVol->SetLineColor(color); + auto longeronLVol = new TGeoVolume(Form("longeronL%d", mNLayer), longeronL, mCarbon); + longeronLVol->SetLineColor(color); + auto zMoveLongerons = new TGeoTranslation(0, 0, -constants::segment::lec::length + (constants::segment::length / 2.)); mCarbonForm->AddNode(HringCVol, 0, zMoveHringC); mCarbonForm->AddNode(HringAVol, 0, zMoveHringA); - mCarbonForm->AddNode(longeronsVol, 0, zMoveLongerons); + mCarbonForm->AddNode(longeronRVol, 0, zMoveLongerons); + mCarbonForm->AddNode(longeronLVol, 0, zMoveLongerons); mCarbonForm->AddNode(mChip, 0); } -TGeoCompositeShape* ITS3Layer::getHringShape(TGeoTubeSeg* Hring) +TGeoCompositeShape* ITS3Layer::getHringShape(TGeoTubeSeg* Hring) const { // Function to dig holes in H-rings using namespace constants::carbonfoam; double stepPhiHoles = (Hring->GetPhi2() - Hring->GetPhi1()) / (nHoles[mNLayer]); - double phiHolesSta = Hring->GetPhi1() + stepPhiHoles / 2.; + double phiHolesSta = Hring->GetPhi1() + (stepPhiHoles / 2.); double radiusHring = 0.5 * (Hring->GetRmin() + Hring->GetRmax()); TGeoCompositeShape* HringWithHoles = nullptr; TString nameAllHoles = ""; for (int iHoles = 0; iHoles < nHoles[mNLayer]; iHoles++) { - double phiHole = phiHolesSta + stepPhiHoles * iHoles; + double phiHole = phiHolesSta + (stepPhiHoles * iHoles); TString nameHole = Form("hole_%d_%d", iHoles, mNLayer); [[maybe_unused]] auto hole = new TGeoTube(nameHole, 0, radiusHoles[mNLayer], 3 * Hring->GetDz()); // move hole to the hring radius @@ -376,9 +378,7 @@ void ITS3Layer::createLayerImpl() void ITS3Layer::buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat, BuildLevel level, bool createMaterials) { - if (!mBuilt) { - getMaterials(createMaterials); - } + getMaterials(createMaterials); switch (level) { case BuildLevel::kPixelArray: createPixelArray(); diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx index cc2255a2b2085..6244ea43a5ca8 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx @@ -13,17 +13,33 @@ /// \brief Definition of the ITS3Services class /// \author Fabrizio Grosa -#include "ITS3Simulation/ITS3Services.h" +#include +#include +#include -#include // for LOG +#include "ITS3Simulation/ITS3Services.h" +#include "ITS3Base/SpecsV2.h" namespace o2::its3 { void ITS3Services::createCYSSAssembly(TGeoVolume* motherVolume) { - // Return the whole assembly - LOGP(info, "Creating CYSS Assembly and attaching to {}", motherVolume->GetName()); + auto cyssVol = new TGeoVolumeAssembly("IBCYSSAssembly"); + cyssVol->SetVisibility(kTRUE); + motherVolume->AddNode(cyssVol, 1., nullptr); + + // Cylinder + auto cyssInnerCylSh = new TGeoTubeSeg(constants::services::radiusInner, constants::services::radiusOuter, constants::services::length / 2, 180, 360); + auto medRohacell = gGeoManager->GetMedium("IT3_RIST110$"); + auto cyssInnerCylShVol = new TGeoVolume("IBCYSSCylinder", cyssInnerCylSh, medRohacell); + cyssInnerCylShVol->SetLineColor(constants::services::color); + cyssVol->AddNode(cyssInnerCylShVol, 1, new TGeoTranslation(0, 0, 0)); + cyssVol->AddNode(cyssInnerCylShVol, 2, new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", 180, 0, 0))); + + // TODO Cone + // For now the wrapping volume just extends beyond the cylinder if something is added beyond that this volume has to + // be exteneded. } } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/study/CMakeLists.txt b/Detectors/Upgrades/ITS3/study/CMakeLists.txt new file mode 100644 index 0000000000000..4bb1cbca7dcb0 --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + +o2_add_library(ITS3TrackingStudy + TARGETVARNAME targetName + SOURCES src/ITS3TrackingStudyParam.cxx + src/TrackingStudy.cxx + src/ParticleInfoExt.cxx + PUBLIC_LINK_LIBRARIES O2::ITS3Workflow + O2::GlobalTracking + O2::GlobalTrackingWorkflowReaders + O2::GlobalTrackingWorkflowHelpers + O2::DataFormatsGlobalTracking + O2::DetectorsVertexing + O2::SimulationDataFormat) + +o2_target_root_dictionary(ITS3TrackingStudy + HEADERS include/ITS3TrackingStudy/ITS3TrackingStudyParam.h + include/ITS3TrackingStudy/ParticleInfoExt.h + LINKDEF src/ITS3TrackingStudyLinkDef.h) + +o2_add_executable(study-workflow + COMPONENT_NAME its3-tracking + SOURCES src/its3-tracking-study-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy) + +add_subdirectory(macros) diff --git a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h new file mode 100644 index 0000000000000..2e718622daa90 --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.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. + +#ifndef O2_TRACKING_STUDY_CONFIG_H +#define O2_TRACKING_STUDY_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +#include "DetectorsBase/Propagator.h" + +namespace o2::its3::study +{ + +struct ITS3TrackingStudyParam : o2::conf::ConfigurableParamHelper { + /// general track selection + float maxChi2{36}; + float maxEta{1.0}; + float minPt{0.1}; + float maxPt{1e2}; + /// PV selection + int minPVCont{5}; + /// ITS track selection + int minITSCls{7}; + bool refitITS{true}; // refit ITS track including the PV + /// TPC track selection + int minTPCCls{110}; + + // propagator + o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; + + /// studies + bool doDCA = true; + bool doDCARefit = true; + bool doPull = true; + bool doMC = false; + O2ParamDef(ITS3TrackingStudyParam, "ITS3TrackingStudyParam"); +}; + +} // namespace o2::its3::study + +#endif diff --git a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ParticleInfoExt.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ParticleInfoExt.h new file mode 100644 index 0000000000000..c66068418377d --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ParticleInfoExt.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 ALICEO2_PARTICLEINFO_EXT_H +#define ALICEO2_PARTICLEINFO_EXT_H + +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "SimulationDataFormat/MCTrack.h" + +namespace o2::its3::study +{ + +struct ParticleInfoExt { + // cluster info + uint8_t clusters{0}; + uint8_t fakeClusters{0}; + // reco info + uint8_t isReco{0}; + uint8_t isFake{0}; + // matching info + uint8_t recoTracks; + uint8_t fakeTracks; + // reco track + track::TrackParCov recoTrack; + // mc info + MCTrack mcTrack; + + ClassDefNV(ParticleInfoExt, 1); +}; + +} // namespace o2::its3::study + +#endif diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h similarity index 63% rename from Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h rename to Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h index 6102ec481c97c..065629058fd32 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h @@ -9,19 +9,17 @@ // 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_ +#ifndef O2_ITS3_TRACKING_STUDY_H +#define O2_ITS3_TRACKING_STUDY_H +#include "ReconstructionDataFormats/GlobalTrackID.h" #include "Framework/DataProcessorSpec.h" -namespace o2 -{ -namespace hmpid +namespace o2::its3::study { -o2::framework::DataProcessorSpec getHMPIDClusterizerSpec(bool useMC); +o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); -} // end namespace hmpid -} // end namespace o2 +} // namespace o2::its3::study -#endif /* STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZERSPEC_H_ */ +#endif diff --git a/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt b/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt new file mode 100644 index 0000000000000..aaf763888c5e0 --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_test_root_macro(PlotDCA.C + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy + LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(PlotPulls.C + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy + LABELS its COMPILE_ONLY) diff --git a/Detectors/Upgrades/ITS3/study/macros/PlotDCA.C b/Detectors/Upgrades/ITS3/study/macros/PlotDCA.C new file mode 100644 index 0000000000000..ac92fa491c1ac --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/PlotDCA.C @@ -0,0 +1,190 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PlotDCA.C +/// \brief Simple macro to plot ITS3 impact parameter resolution + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include + +#include +#include +#include +#include +#include +#include + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#endif + +using GTrackID = o2::dataformats::GlobalTrackID; + +static std::string SanitizeSourceName(std::string_view raw) +{ + std::string s(raw); + s.erase(std::remove(s.begin(), s.end(), '-'), s.end()); + return s; +} + +void PlotDCA(const char* fName = "its3TrackStudy.root") +{ + TH1::SetDefaultSumw2(); + std::unique_ptr inFile(TFile::Open(fName)); + auto tree = inFile->Get("dca"); + + int src; // track type + tree->SetBranchAddress("src", &src); + o2::dataformats::DCA* dca{nullptr}; + tree->SetBranchAddress("dca2MC", &dca); + o2::track::TrackParCov* trk{nullptr}; + tree->SetBranchAddress("trk", &trk); + o2::MCTrack* mcTrk{nullptr}; + tree->SetBranchAddress("mcTrk", &mcTrk); + + const int nPtBins = 35; + const double ptLimits[nPtBins] = {0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2., 2.2, 2.5, 3., 4., 5., 6., 8., 10., 15., 20.}; + const int yDCABins{1000}; + const float yDCARange{500}; + const int nSpecies = 5; + std::array pdgCodes{-1, 11, 211, 321, 2212}; + auto fGaus = new TF1("fGaus", "gaus", -200., 200.); + std::map partNames = { + {-1, "All"}, + {11, "Electrons"}, + {211, "Pions"}, + {321, "Kaons"}, + {2212, "Protons"}}; + + std::map> hMapDCAxyVsPtAllLayers; // species -> [src, {dca}] + std::map> hMapResDCAxyVsPtAllLayers; // species -> [src, {dca}] + std::map> hMapDCAzVsPtAllLayers; // species -> [src, {dca}] + std::map> hMapResDCAzVsPtAllLayers; // species -> [src, {dca}] + std::map> hMapDeltaPtVsPtAllLayers; // species -> [src, {dca}] + std::map> hMapResPtVsPtAllLayers; // species -> [src, {dca}] + for (const auto& [sPDG, sName] : partNames) { + std::map histsDCAxy, histsDCAz, histsDeltaPt; + std::map histsResDCAxy, histsResDCAz, histsResDeltaPt; + + for (int cis = 0; cis < GTrackID::NSources; ++cis) { + const auto cdm = GTrackID::getSourceDetectorsMask(cis); + if (!cdm[GTrackID::ITS]) { + continue; // keep same logic as original + } + + const std::string srcRaw = GTrackID::getSourceName(cis); + const std::string src = SanitizeSourceName(srcRaw); + + histsDCAxy[cis] = new TH2F(Form("hDCAxyVsPtAllLayers_%s_%s", sName.c_str(), src.c_str()), Form("%s;#it{p}_{T,MC} (GeV/#it{c});DCA_{#it{xy}} (#mum);entries", srcRaw.c_str()), nPtBins - 1, ptLimits, yDCABins, -yDCARange, yDCARange); + + histsResDCAxy[cis] = new TH1F(Form("hResDCAxyVsPtAllLayers_%s_%s", sName.c_str(), src.c_str()), Form("%s;#it{p}_{T,MC} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum);entries", srcRaw.c_str()), nPtBins - 1, ptLimits); + + histsDCAz[cis] = new TH2F(Form("hDCAzVsPtAllLayers_%s_%s", sName.c_str(), src.c_str()), Form("%s;#it{p}_{T,MC} (GeV/#it{c});DCA_{#it{z}} (#mum);entries", srcRaw.c_str()), nPtBins - 1, ptLimits, yDCABins, -yDCARange, yDCARange); + + histsResDCAz[cis] = new TH1F(Form("hResDCAzVsPtAllLayers_%s_%s", sName.c_str(), src.c_str()), Form("%s;#it{p}_{T,MC} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum);entries", srcRaw.c_str()), nPtBins - 1, ptLimits); + + histsDeltaPt[cis] = new TH2F(Form("hDeltaPtVsPtAllLayers_%s_%s", sName.c_str(), src.c_str()), Form("%s;#it{p}_{T,MC} (GeV/#it{c});#Delta_{#it{p}_{T}}/#it{p}_{T};entries", srcRaw.c_str()), nPtBins - 1, ptLimits, 200, -0.2, 0.2); + + histsResDeltaPt[cis] = new TH1F(Form("hResDeltaPtVsPtAllLayers_%s_%s", sName.c_str(), src.c_str()), Form("%s;#it{p}_{T,MC} (GeV/#it{c});#sigma(#Delta#it{p}_{T}/#it{p}_{T});entries", srcRaw.c_str()), nPtBins - 1, ptLimits); + } + + hMapDCAxyVsPtAllLayers[sPDG] = std::move(histsDCAxy); + hMapResDCAxyVsPtAllLayers[sPDG] = std::move(histsResDCAxy); + hMapDCAzVsPtAllLayers[sPDG] = std::move(histsDCAz); + hMapResDCAzVsPtAllLayers[sPDG] = std::move(histsResDCAz); + hMapDeltaPtVsPtAllLayers[sPDG] = std::move(histsDeltaPt); + hMapResPtVsPtAllLayers[sPDG] = std::move(histsResDeltaPt); + } + + for (int iEntry = 0; tree->LoadTree(iEntry) >= 0; ++iEntry) { + tree->GetEntry(iEntry); + if (!mcTrk->isPrimary()) { + continue; + } + auto pdg = std::abs(mcTrk->GetPdgCode()); + if (pdg != 11 && pdg != 211 && pdg != 321 && pdg != 2212) { + continue; + } + auto ptReco = trk->getPt(); + auto ptGen = mcTrk->GetPt(); + auto deltaPt = (1. / ptReco - 1. / ptGen) / (1. / ptGen); + auto dcaXY = dca->getY() * 10000.; + auto dcaZ = dca->getZ() * 10000.; + auto phiReco = trk->getPhi(); + + for (int spe : {-1, pdg}) { + hMapDeltaPtVsPtAllLayers[spe][src]->Fill(ptGen, deltaPt); + hMapDCAxyVsPtAllLayers[spe][src]->Fill(ptGen, dcaXY); + hMapDCAzVsPtAllLayers[spe][src]->Fill(ptGen, dcaZ); + } + } + + const char* fitOpt{"QWMER"}; + for (const auto& [sPDG, sName] : partNames) { + for (int cis = 0; cis < GTrackID::NSources; cis++) { + const auto cdm = GTrackID::getSourceDetectorsMask(cis); + if (!cdm[GTrackID::ITS]) { + continue; + } + for (auto iPt{0}; iPt < nPtBins; ++iPt) { + auto ptMin = hMapDCAxyVsPtAllLayers[sPDG][cis]->GetXaxis()->GetBinLowEdge(iPt + 1); + float minFit = (ptMin < 1.) ? -200. : -50.; + float maxFit = (ptMin < 1.) ? 200. : 50.; + auto doProjection = [&](auto& hIn, auto& hOut, bool useRange = true) { + auto hProj = hIn[sPDG][cis]->ProjectionY(Form("%s_%d", hOut[sPDG][cis]->GetName(), iPt), iPt + 1, iPt + 1); + if (hProj->GetEntries() < 100) { + return; + } + if (useRange) { + hProj->Fit("fGaus", fitOpt, "", minFit, maxFit); + } else { + hProj->Fit("fGaus", fitOpt); + } + hOut[sPDG][cis]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); + hOut[sPDG][cis]->SetBinError(iPt + 1, fGaus->GetParError(2)); + }; + + doProjection(hMapDeltaPtVsPtAllLayers, hMapResPtVsPtAllLayers, false); + doProjection(hMapDCAxyVsPtAllLayers, hMapResDCAxyVsPtAllLayers); + doProjection(hMapDCAzVsPtAllLayers, hMapResDCAzVsPtAllLayers); + } + } + } + + TFile outFile("plotDCA.root", "RECREATE"); + for (const auto& [sPDG, sName] : partNames) { + outFile.mkdir(sName.c_str()); + outFile.cd(sName.c_str()); + for (int cis = 0; cis < GTrackID::NSources; cis++) { + const auto cdm = GTrackID::getSourceDetectorsMask(cis); + if (!cdm[GTrackID::ITS]) { + continue; + } + const std::string srcRaw = GTrackID::getSourceName(cis); + const std::string src = SanitizeSourceName(srcRaw); + gDirectory->mkdir(src.c_str()); + gDirectory->cd(src.c_str()); + + hMapDCAxyVsPtAllLayers[sPDG][cis]->Write(); + hMapResDCAxyVsPtAllLayers[sPDG][cis]->Write(); + hMapDCAzVsPtAllLayers[sPDG][cis]->Write(); + hMapResDCAzVsPtAllLayers[sPDG][cis]->Write(); + hMapDeltaPtVsPtAllLayers[sPDG][cis]->Write(); + hMapResPtVsPtAllLayers[sPDG][cis]->Write(); + + outFile.cd(sName.c_str()); + } + + outFile.cd(); + } +} diff --git a/Detectors/Upgrades/ITS3/study/macros/PlotPulls.C b/Detectors/Upgrades/ITS3/study/macros/PlotPulls.C new file mode 100644 index 0000000000000..371a94cda0e70 --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/PlotPulls.C @@ -0,0 +1,176 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PlotDCA.C +/// \brief Simple macro to plot ITS3 impact parameter resolution + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#endif + +// 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)); +} + +void PlotPulls(const char* fName = "its3TrackStudy.root") +{ + TH1::SetDefaultSumw2(); + std::unique_ptr inFile(TFile::Open(fName)); + auto tree = inFile->Get("pull"); + + uint8_t src; // track type + tree->SetBranchAddress("src", &src); + o2::track::TrackParCov* trk{nullptr}; + tree->SetBranchAddress("trk", &trk); + o2::track::TrackPar* mcTrk{nullptr}; + tree->SetBranchAddress("mcTrk", &mcTrk); + o2::MCTrack* part{nullptr}; + tree->SetBranchAddress("mcPart", &part); + const int nPtBins = 35; + const double ptLimits[nPtBins] = {0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2., 2.2, 2.5, 3., 4., 5., 6., 8., 10., 15., 20.}; + const int yBins{100}, yRange{5}; + const char* pNames[5] = {"Y", "Z", "Snp", "Tgl", "Q2Pt"}; + auto fGaus = new TF1("fGaus", "[0]*exp(-0.5*((x-[1])/[2])**2)", -3., 3.); + + std::array pulls{ + new TH2F("hPullY", "", nPtBins - 1, ptLimits, yBins, -yRange, yRange), + new TH2F("hPullZ", "", nPtBins - 1, ptLimits, yBins, -yRange, yRange), + new TH2F("hPullSnp", "", nPtBins - 1, ptLimits, yBins, -yRange, yRange), + new TH2F("hPullTgl", "", nPtBins - 1, ptLimits, yBins, -yRange, yRange), + new TH2F("hPullQ2Pt", "", nPtBins - 1, ptLimits, yBins, -yRange, yRange)}; + + std::array means{ + new TH1F("hPullYMean", "", nPtBins - 1, ptLimits), + new TH1F("hPullZMean", "", nPtBins - 1, ptLimits), + new TH1F("hPullSnpMean", "", nPtBins - 1, ptLimits), + new TH1F("hPullTglMean", "", nPtBins - 1, ptLimits), + new TH1F("hPullQ2PtMean", "", nPtBins - 1, ptLimits)}; + + std::array sigmas{ + new TH1F("hPullYSigma", "", nPtBins - 1, ptLimits), + new TH1F("hPullZSigma", "", nPtBins - 1, ptLimits), + new TH1F("hPullSnpSigma", "", nPtBins - 1, ptLimits), + new TH1F("hPullTglSigma", "", nPtBins - 1, ptLimits), + new TH1F("hPullQ2PtSigma", "", nPtBins - 1, ptLimits)}; + + auto calcMahalanobisDist2 = [&](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 ROOT::Math::Similarity(cov, res); + }; + + auto hMahDist2 = new TH1F("hMahDist2", ";Mahalanobis distance 2;n. entries", 100, 0, 10); + + auto getIndex = [](int i) -> int { return i * (i + 3) / 2; }; + + for (int iEntry = 0; tree->LoadTree(iEntry) >= 0; ++iEntry) { + tree->GetEntry(iEntry); + if (src != o2::dataformats::GlobalTrackID::ITS || std::abs(part->GetPdgCode()) != 211) { + continue; + } + for (int i{0}; i < o2::track::kNParams; ++i) { + pulls[i]->Fill(part->GetPt(), (trk->getParam(i) - mcTrk->getParam(i)) / std::sqrt(trk->getCov()[getIndex(i)])); + } + if (part->GetPt() >= 1.0 && part->GetPt() < 2) { + if (auto dist = calcMahalanobisDist2(trk, mcTrk); dist >= 0.) { + hMahDist2->Fill(dist); + } + } + } + + std::vector projs; + const char* fitOpt{"QWMERSB"}; + for (int i{0}; i < o2::track::kNParams; ++i) { + for (auto iPt{0}; iPt < nPtBins; ++iPt) { + auto hProj = pulls[i]->ProjectionY(Form("%s_%d", pulls[i]->GetName(), iPt), iPt + 1, iPt + 1); + hProj->SetName(Form("p%s_pt%d", pNames[i], iPt)); + hProj->SetTitle(Form("Pull %s #it{p}_{T}#in[%.2f, %.2f)", pNames[i], ptLimits[iPt], ptLimits[iPt + 1])); + projs.push_back(hProj); + if (hProj->GetEntries() < 100) { + return; + } + fGaus->SetParameter(1, 0); + fGaus->SetParameter(2, 1); + auto fRes = hProj->Fit(fGaus, fitOpt); + if (fRes->IsValid() && fGaus->GetParameter(2) > 0) { + means[i]->SetBinContent(iPt + 1, fGaus->GetParameter(1)); + means[i]->SetBinError(iPt + 1, fGaus->GetParError(1)); + sigmas[i]->SetBinContent(iPt + 1, fGaus->GetParameter(2)); + sigmas[i]->SetBinError(iPt + 1, fGaus->GetParError(2)); + } + } + } + + hMahDist2->Scale(1. / hMahDist2->Integral("width")); + TF1* fchi2Fit = new TF1("fchi2_fit", chi2_pdf, 0.1, 6, 3); + fchi2Fit->SetParNames("A", "k", "s"); + fchi2Fit->SetParameter(0, 1); + fchi2Fit->SetParameter(1, 5); + fchi2Fit->SetParameter(2, 1); + auto fitres = hMahDist2->Fit(fchi2Fit, "RMQS"); + fitres->Print(); + + TFile outFile("plotPulls.root", "RECREATE"); + for (int i{0}; i < o2::track::kNParams; ++i) { + pulls[i]->Write(); + means[i]->Write(); + sigmas[i]->Write(); + } + for (const auto& p : projs) { + p->Write(); + } + hMahDist2->Write(); + fchi2Fit->Write(); +} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h b/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyLinkDef.h similarity index 68% rename from Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h rename to Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyLinkDef.h index 1e8596fbb224c..182ffd858629c 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h +++ b/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyLinkDef.h @@ -15,12 +15,9 @@ #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 + ; +#pragma link C++ class o2::its3::study::ITS3TrackingStudyParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its3::study::ITS3TrackingStudyParam> + ; + +#pragma link C++ class o2::its3::study::ParticleInfoExt + ; #endif diff --git a/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyParam.cxx b/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyParam.cxx new file mode 100644 index 0000000000000..00bb800e65f8c --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyParam.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 "ITS3TrackingStudy/ITS3TrackingStudyParam.h" +O2ParamImpl(o2::its3::study::ITS3TrackingStudyParam); diff --git a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx b/Detectors/Upgrades/ITS3/study/src/ParticleInfoExt.cxx similarity index 85% rename from DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx rename to Detectors/Upgrades/ITS3/study/src/ParticleInfoExt.cxx index bcf10d74c95ff..aa5edbf408270 100644 --- a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx +++ b/Detectors/Upgrades/ITS3/study/src/ParticleInfoExt.cxx @@ -9,8 +9,5 @@ // 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 -{ -} +#include "ITS3TrackingStudy/ParticleInfoExt.h" +ClassImp(o2::its3::study::ParticleInfoExt); diff --git a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx new file mode 100644 index 0000000000000..cb1d7f381983d --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx @@ -0,0 +1,841 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +#include +#include + +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "DataFormatsITSMFT/Digit.h" +#include "ITSMFTSimulation/Hit.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/SimTraits.h" +#include "DetectorsVertexing/PVertexer.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITS3Base/SpecsV2.h" +#include "ITS3Reconstruction/TopologyDictionary.h" +#include "ITS3Reconstruction/IOUtils.h" +#include "ITS3TrackingStudy/ITS3TrackingStudyParam.h" +#include "ITS3TrackingStudy/ParticleInfoExt.h" +#include "ReconstructionDataFormats/DCA.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "Steer/MCKinematicsReader.h" + +namespace o2::its3::study +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; +using PVertex = o2::dataformats::PrimaryVertex; +using GTrackID = o2::dataformats::GlobalTrackID; +using VtxTrackID = o2::dataformats::VtxTrackIndex; +using T2VMap = std::unordered_map; + +class TrackingStudySpec : public Task +{ + public: + TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} + ~TrackingStudySpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + + private: + void process(o2::globaltracking::RecoContainer& recoData); + void updateTimeDependentParams(ProcessingContext& pc); + std::vector> prepareITSClusters(const o2::globaltracking::RecoContainer& data) const; + bool selectTrack(GTrackID trkID, o2::globaltracking::RecoContainer& recoData, bool checkMCTruth = true) const; + T2VMap buildT2V(o2::globaltracking::RecoContainer& recoData, bool includeCont = false, bool requireMCMatch = true) const; + bool refitITSPVTrack(o2::globaltracking::RecoContainer& recoData, o2::track::TrackParCov& trFit, GTrackID gidx); + void doDCAStudy(o2::globaltracking::RecoContainer& recoData); + void doDCARefitStudy(o2::globaltracking::RecoContainer& recoData); + void doPullStudy(o2::globaltracking::RecoContainer& recoData); + void doMCStudy(o2::globaltracking::RecoContainer& recoData); + + struct TrackCounter { + TrackCounter() = default; + + void operator+=(int src) + { + if (src >= 0 && src < static_cast(mSuccess.size())) { + ++mSuccess[src]; + } + } + + void operator-=(int src) + { + if (src >= 0 && src < static_cast(mirrors.size())) { + ++mirrors[src]; + } + } + + void operator&=(int src) + { + if (src >= 0 && src < static_cast(mRejected.size())) { + ++mRejected[src]; + } + } + + void print() const + { + LOGP(info, "\t\t\tSuccess / Error / Rejected"); + for (int cis = 0; cis < GTrackID::NSources; ++cis) { + const auto cdm = GTrackID::getSourceDetectorsMask(cis); + if (cdm[DetID::ITS]) { + LOGP(info, "\t{:{}}\t{} / {} / {}", GTrackID::getSourceName(cis), 15, mSuccess[cis], mirrors[cis], mRejected[cis]); + } + } + } + + void reset() + { + mSuccess.fill(0); + mirrors.fill(0); + mRejected.fill(0); + } + + std::array mSuccess{}; + std::array mirrors{}; + std::array mRejected{}; + }; + TrackCounter mTrackCounter; + + std::unique_ptr mDBGOut; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC{false}; + GTrackID::mask_t mTracksSrc; + o2::vertexing::PVertexer mVertexer; + o2::steer::MCKinematicsReader mcReader; // reader of MC information + const o2::its3::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary +}; + +void TrackingStudySpec::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 ? "its3TrackStudy.root" : fmt::format("its3TrackStudy_{}.root", lane); + mDBGOut = std::make_unique(dbgnm.c_str(), "recreate"); + + if (mUseMC && !mcReader.initFromDigitContext(o2::base::NameConf::getCollisionContextFileName())) { + LOGP(fatal, "initialization of MCKinematicsReader failed"); + } +} + +void TrackingStudySpec::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest); + updateTimeDependentParams(pc); + process(recoData); +} + +void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (static bool initOnceDone{false}; !initOnceDone) { // this params need to be queried only once + initOnceDone = true; + auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + mVertexer.init(); + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + } +} + +void TrackingStudySpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::its3::TopologyDictionary*)obj; + return; + } +} + +void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) +{ + const auto& conf = ITS3TrackingStudyParam::Instance(); + if (conf.doDCA) { + doDCAStudy(recoData); + } + if (conf.doDCARefit) { + doDCARefitStudy(recoData); + } + if (mUseMC && conf.doPull) { + doPullStudy(recoData); + } + if (mUseMC && conf.doMC) { + doMCStudy(recoData); + } +} + +std::vector> TrackingStudySpec::prepareITSClusters(const o2::globaltracking::RecoContainer& data) const +{ + std::vector> itscl; + const auto& clusITS = data.getITSClusters(); + if (clusITS.size()) { + const auto& patterns = data.getITSClustersPatterns(); + itscl.reserve(clusITS.size()); + auto pattIt = patterns.begin(); + o2::its3::ioutils::convertCompactClusters(clusITS, pattIt, itscl, mITSDict); + } + return std::move(itscl); +} + +bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoContainer& recoData, bool checkMCTruth) const +{ + const auto& conf = ITS3TrackingStudyParam::Instance(); + if (!trkID.includesDet(GTrackID::ITS)) { + return false; + } + if (!recoData.isTrackSourceLoaded(trkID.getSource())) { + return false; + } + auto contributorsGID = recoData.getSingleDetectorRefs(trkID); + if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS + return false; + } + // ITS specific + const auto& itsTrk = recoData.getITSTrack(contributorsGID[GTrackID::ITS]); + if (itsTrk.getChi2() > conf.maxChi2 || itsTrk.getNClusters() < conf.minITSCls) { + return false; + } + // TPC specific + if (contributorsGID[GTrackID::TPC].isIndexSet()) { + const auto& tpcTrk = recoData.getTPCTrack(contributorsGID[GTrackID::TPC]); + if (tpcTrk.getNClusters() < conf.minTPCCls) { + return false; + } + } + // general + const auto& gTrk = recoData.getTrackParam(trkID); + if (gTrk.getPt() < conf.minPt || gTrk.getPt() > conf.maxPt) { + return false; + } + if (std::abs(gTrk.getEta()) > conf.maxEta) { + return false; + } + if (mUseMC && checkMCTruth) { + const auto& itsLbl = recoData.getTrackMCLabel(contributorsGID[GTrackID::ITS]); + if (!itsLbl.isValid()) { + return false; + } + if (contributorsGID[GTrackID::TPC].isIndexSet()) { + const auto& tpcLbl = recoData.getTrackMCLabel(contributorsGID[GTrackID::TPC]); + if (itsLbl != tpcLbl) { + return false; + } + } + if (contributorsGID[GTrackID::TRD].isIndexSet()) { + // TODO + } + if (contributorsGID[GTrackID::TOF].isIndexSet()) { + const auto& tofLbls = recoData.getTOFClustersMCLabels()->getLabels(contributorsGID[GTrackID::TOF]); + for (const auto& lbl : tofLbls) { + if (lbl.isValid()) { + return true; + } + } + } + } + return true; +} + +T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, bool includeCont, bool requireMCMatch) const +{ + // build track->vertex assoc., maybe including contributor tracks + const auto& conf = ITS3TrackingStudyParam::Instance(); + auto pvvec = recoData.getPrimaryVertices(); + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + T2VMap t2v; + for (size_t iv = 0; iv < nv; ++iv) { + const auto& pv = pvvec[iv]; + if (pv.getNContributors() - 1 < conf.minPVCont) { + continue; + } + if (requireMCMatch) { + auto pvl = recoData.getPrimaryVertexMCLabel(iv); + } + const auto& vtxRef = vtxRefs[iv]; + int it = vtxRef.getFirstEntry(), itLim = it + vtxRef.getEntries(); + for (; it < itLim; it++) { + const auto& tvid = trackIndex[it]; + if (tvid.isAmbiguous()) { + continue; + } + if (!recoData.isTrackSourceLoaded(tvid.getSource())) { + continue; + } + if (mUseMC && requireMCMatch) { + const auto& pvlbl = recoData.getPrimaryVertexMCLabel(iv); + if (pvlbl.getEventID() != recoData.getTrackMCLabel(tvid).getEventID()) { + continue; + } + } + t2v[tvid] = iv; + if (includeCont) { + auto contributorsGID = recoData.getSingleDetectorRefs(tvid); + for (int cis = 0; cis < GTrackID::NSources; cis++) { + const auto cdm = GTrackID::getSourceDetectorsMask(cis); + if (!recoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { + continue; + } + if (mUseMC && requireMCMatch) { + const auto& pvlbl = recoData.getPrimaryVertexMCLabel(iv); + if (pvlbl.getEventID() != recoData.getTrackMCLabel(contributorsGID[cis]).getEventID()) { + continue; + } + } + t2v[contributorsGID[cis]] = iv; + } + } + } + } + return std::move(t2v); +} + +bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoData, o2::track::TrackParCov& trFit, GTrackID gidx) +{ + if (gidx.getSource() != GTrackID::ITS) { + return false; + } + static auto pvvec = recoData.getPrimaryVertices(); + static auto t2v = buildT2V(recoData, true, true); + static const auto itsClusters = prepareITSClusters(recoData); + static std::vector itsTracksROF; + if (static bool done{false}; !done) { + done = true; + const auto& itsTracksROFRec = recoData.getITSTracksROFRecords(); + itsTracksROF.resize(recoData.getITSTracks().size()); + for (unsigned irf = 0, cnt = 0; irf < itsTracksROFRec.size(); irf++) { + int ntr = itsTracksROFRec[irf].getNEntries(); + for (int itr = 0; itr < ntr; itr++) { + itsTracksROF[cnt++] = irf; + } + } + } + auto prop = o2::base::Propagator::Instance(); + const auto& conf = ITS3TrackingStudyParam::Instance(); + std::array, 8> clArr{}; + std::array clAlpha{}; + const auto trkIn = recoData.getTrackParam(gidx); + const auto trkOut = recoData.getTrackParamOut(gidx); + const auto& itsTrOrig = recoData.getITSTrack(gidx); + int ncl = itsTrOrig.getNumberOfClusters(), rof = itsTracksROF[gidx.getIndex()]; + const auto& itsTrackClusRefs = recoData.getITSTracksClusterRefs(); + int clEntry = itsTrOrig.getFirstClusterEntry(); + const auto propagator = o2::base::Propagator::Instance(); + // convert PV to a fake cluster in the track DCA frame + const auto& pv = pvvec[t2v[gidx]]; + auto trkPV = trkIn; + if (!prop->propagateToDCA(pv, trkPV, prop->getNominalBz(), 2.0, conf.CorrType)) { + mTrackCounter -= gidx.getSource(); + return false; + } + // create base cluster from the PV, with the alpha corresponding to the track at DCA + float cosAlp = NAN, sinAlp = NAN; + o2::math_utils::sincos(trkPV.getAlpha(), sinAlp, cosAlp); + // vertex position rotated to track frame + clArr[0].setXYZ(pv.getX() * cosAlp + pv.getY() * sinAlp, -pv.getX() * sinAlp + pv.getY() * cosAlp, pv.getZ()); + clArr[0].setSigmaY2(0.5 * (pv.getSigmaX2() + pv.getSigmaY2())); + clArr[0].setSigmaZ2(pv.getSigmaZ2()); + clAlpha[0] = trkPV.getAlpha(); + for (int icl = 0; icl < ncl; ++icl) { // ITS clusters are referred in layer decreasing order + clArr[ncl - icl] = itsClusters[itsTrackClusRefs[clEntry + icl]]; + clAlpha[ncl - icl] = o2::its::GeometryTGeo::Instance()->getSensorRefAlpha(clArr[ncl - icl].getSensorID()); + } + // start refit + trFit = trkOut; + trFit.resetCovariance(1'000); + float chi2{0}; + for (int icl = ncl; icl >= 0; --icl) { // go backwards + if (!trFit.rotate(clAlpha[icl]) || !prop->propagateToX(trFit, clArr[icl].getX(), prop->getNominalBz(), 0.85, 2.0, conf.CorrType)) { + mTrackCounter -= gidx.getSource(); + return false; + } + chi2 += trFit.getPredictedChi2(clArr[icl]); + if (!trFit.update(clArr[icl])) { + mTrackCounter -= gidx.getSource(); + return false; + } + } + // chi2 < conf.maxChi2; should I cut here? + return true; +}; + +void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) +{ + /// analyse DCA of impact parameter for different track types + LOGP(info, "Doing DCA study"); + mTrackCounter.reset(); + const auto& conf = ITS3TrackingStudyParam::Instance(); + auto prop = o2::base::Propagator::Instance(); + TStopwatch sw; + sw.Start(); + int nDCAFits{0}, nDCAFitsFail{0}; + auto pvvec = recoData.getPrimaryVertices(); + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + auto& stream = (*mDBGOut) << "dca"; + for (int iv = 0; iv < nv; iv++) { + const auto& pv = pvvec[iv]; + const auto& vtref = vtxRefs[iv]; + for (int is = 0; is < GTrackID::NSources; is++) { + const auto dm = GTrackID::getSourceDetectorsMask(is); + if (!recoData.isTrackSourceLoaded(is) || !dm[DetID::ITS]) { + mTrackCounter &= is; + continue; + } + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); + for (int i = idMin; i < idMax; i++) { + const auto vid = trackIndex[i]; + if (!vid.isPVContributor()) { + mTrackCounter &= vid.getSource(); + continue; + } + + // we fit each different sub-track type, that include ITS, e.g. + // ITS,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF + auto contributorsGID = recoData.getSingleDetectorRefs(vid); + for (int cis = 0; cis < GTrackID::NSources && cis <= is; cis++) { + const auto cdm = GTrackID::getSourceDetectorsMask(cis); + if (!recoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { + mTrackCounter &= cis; + continue; + } + if (!selectTrack(contributorsGID[cis], recoData)) { + mTrackCounter &= vid.getSource(); + continue; + } + + o2::dataformats::DCA dcaInfo; + const auto& trk = recoData.getTrackParam(contributorsGID[cis]); + auto trkRefit = trk; + // for ITS standalone tracks instead of having the trk at the pv we refit with the pv + if (conf.refitITS && cis == GTrackID::ITS && !refitITSPVTrack(recoData, trkRefit, contributorsGID[cis])) { + mTrackCounter -= cis; + continue; + } else { + trkRefit.invalidate(); + }; + + auto trkDCA = trk; + if (!prop->propagateToDCABxByBz(pv, trkDCA, 2.f, conf.CorrType, &dcaInfo)) { + mTrackCounter -= cis; + ++nDCAFitsFail; + continue; + } + + stream << "src=" << cis + << "pv=" << pv + << "trk=" << trk + << "trkRefit=" << trkRefit + << "trkAtPV=" << trkDCA + << "dca=" << dcaInfo; + + if (mUseMC) { + const auto& lbl = recoData.getTrackMCLabel(contributorsGID[cis]); + lbl.print(); + o2::dataformats::DCA dcaInfoMC; + const auto& eve = mcReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + o2::dataformats::VertexBase mcEve; + mcEve.setPos({(float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()}); + auto trkC = trk; + if (!prop->propagateToDCABxByBz(mcEve, trkC, 2.f, conf.CorrType, &dcaInfoMC)) { + mTrackCounter -= cis; + ++nDCAFitsFail; + continue; + } + const auto& mcTrk = mcReader.getTrack(lbl); + if (mcTrk == nullptr) { + LOGP(fatal, "mcTrk is null did selection fail?"); + } + stream << "mcTrk=" << *mcTrk + << "dca2MC=" << dcaInfoMC + << "lbl=" << lbl; + } + stream << "\n"; + + ++nDCAFits; + mTrackCounter += cis; + } + } + } + } + sw.Stop(); + LOGP(info, "doDCAStudy: accepted {} fits, failed {} (in {:.2f} seconds)", nDCAFits, nDCAFitsFail, sw.RealTime()); + mTrackCounter.print(); +} + +void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoData) +{ + /// analyse DCA of impact parameter for different track types while refitting the PV without the cand track + LOGP(info, "Doing DCARefit study"); + mTrackCounter.reset(); + const auto& conf = ITS3TrackingStudyParam::Instance(); + auto prop = o2::base::Propagator::Instance(); + TStopwatch sw; + sw.Start(); + + // build track->vertex assoc. + auto pvvec = recoData.getPrimaryVertices(); + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + auto t2v = buildT2V(recoData); + std::vector> v2t; + v2t.resize(nv); + auto creator = [&](const auto& trk, GTrackID trkID, float _t0, float terr) -> bool { + if constexpr (!isBarrelTrack()) { + mTrackCounter &= trkID.getSource(); + return false; + } + if (!trkID.includesDet(GTrackID::ITS)) { + mTrackCounter &= trkID.getSource(); + return false; + } + // general + if constexpr (isBarrelTrack()) { + if (trk.getPt() < conf.minPt || trk.getPt() > conf.maxPt) { + mTrackCounter &= trkID.getSource(); + return false; + } + if (std::abs(trk.getEta()) > conf.maxEta) { + mTrackCounter &= trkID.getSource(); + return false; + } + if (!t2v.contains(trkID)) { + mTrackCounter &= trkID.getSource(); + return false; + } + if (!selectTrack(trkID, recoData, mUseMC)) { + mTrackCounter &= trkID.getSource(); + return false; + } + } + v2t[t2v[trkID]].push_back(trkID); + return true; + }; + recoData.createTracksVariadic(creator); + + int nDCAFits{0}, nDCAFitsFail{0}; + auto& stream = (*mDBGOut) << "dcaRefit"; + for (size_t iv = 0; iv < nv; ++iv) { + const auto& pv = pvvec[iv]; + const auto& trkIDs = v2t[iv]; + if (trkIDs.size() - 1 < conf.minPVCont) { + continue; + } + std::vector trks; + trks.reserve(trkIDs.size()); + for (const auto& trkID : trkIDs) { + trks.push_back(recoData.getTrackParam(trkID)); + } + + if (!mVertexer.prepareVertexRefit(trks, pv)) { + continue; + } + std::vector trkMask(trkIDs.size(), true); + for (size_t it{0}; it < trkMask.size(); ++it) { + trkMask[it] = false; // mask current track from pv refit + if (it != 0) { + trkMask[it - 1] = true; // unmask previoustrack from pv refit + } + auto pvRefit = mVertexer.refitVertex(trkMask, pv); + if (pvRefit.getChi2() < 0) { + trkMask[it] = true; + continue; + } + + // check DCA both for refitted and original PV + o2::dataformats::DCA dcaInfo; + auto trkC = trks[it]; + if (!prop->propagateToDCABxByBz(pv, trkC, 2.f, conf.CorrType, &dcaInfo)) { + mTrackCounter -= trkIDs[it].getSource(); + ++nDCAFitsFail; + continue; + } + o2::dataformats::DCA dcaInfoRefit; + auto trkCRefit = trks[it]; + if (!prop->propagateToDCABxByBz(pv, trkCRefit, 2.f, conf.CorrType, &dcaInfoRefit)) { + mTrackCounter -= trkIDs[it].getSource(); + ++nDCAFitsFail; + continue; + } + + stream << "src=" << trkIDs[it].getSource() + << "pv=" << pv + << "trkAtPV=" << trkC + << "dca=" << dcaInfo + << "pvRefit=" << pvRefit + << "trkAtPVRefit=" << trkC + << "dcaRefit=" << dcaInfoRefit; + if (mUseMC) { + const auto& mcTrk = mcReader.getTrack(recoData.getTrackMCLabel(trkIDs[it])); + if (mcTrk == nullptr) { + LOGP(fatal, "mcTrk is null did selection fail?"); + } + stream << "mcTrk=" << *mcTrk; + } + stream << "\n"; + ++nDCAFits; + mTrackCounter += trkIDs[it].getSource(); + } + } + sw.Stop(); + LOGP(info, "doDCARefitStudy: accepted {} fits, failed {} (in {:.2f} seconds)", nDCAFits, nDCAFitsFail, sw.RealTime()); + mTrackCounter.print(); +} + +void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) +{ + // check track pulls compared to mc generation + LOGP(info, "Doing Pull study"); + mTrackCounter.reset(); + TStopwatch sw; + sw.Start(); + int nPulls{0}, nPullsFail{0}; + auto prop = o2::base::Propagator::Instance(); + const auto& conf = ITS3TrackingStudyParam::Instance(); + + auto checkInTrack = [&](GTrackID trkID) { + if (!selectTrack(trkID, recoData)) { + mTrackCounter &= trkID.getSource(); + return; + } + const auto mcTrk = mcReader.getTrack(recoData.getTrackMCLabel(trkID)); + if (!mcTrk) { + return; + } + auto trk = recoData.getTrackParam(trkID); + + // for ITS standalone tracks we add the PV as an additional measurement point + if (conf.refitITS && trkID.getSource() == GTrackID::ITS && !refitITSPVTrack(recoData, trk, trkID)) { + mTrackCounter -= trkID.getSource(); + ++nPullsFail; + return; + } + + std::array xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()}, + pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (!pPDG) { + mTrackCounter -= trkID.getSource(); + ++nPullsFail; + return; + } + o2::track::TrackPar mcTrkO2(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + // propagate it to the alpha/X of the reconstructed track + if (!mcTrkO2.rotate(trk.getAlpha()) || !prop->PropagateToXBxByBz(mcTrkO2, trk.getX())) { + mTrackCounter -= trkID.getSource(); + ++nPullsFail; + return; + } + const auto contTrk = recoData.getSingleDetectorRefs(trkID); + const auto& itsTrk = recoData.getITSTrack(contTrk[GTrackID::ITS]); + + (*mDBGOut) + << "pull" + << "src=" << trkID.getSource() + << "itsTrk=" << itsTrk + << "mcTrk=" << mcTrkO2 + << "mcPart=" << mcTrk + << "trk=" << trk + << "\n"; + ++nPulls; + mTrackCounter += trkID.getSource(); + }; + + for (size_t iTrk{0}; iTrk < recoData.getITSTracks().size(); ++iTrk) { + checkInTrack(GTrackID(iTrk, GTrackID::ITS)); + } + for (size_t iTrk{0}; iTrk < recoData.getTPCITSTracks().size(); ++iTrk) { + checkInTrack(GTrackID(iTrk, GTrackID::ITSTPC)); + } + for (size_t iTrk{0}; iTrk < recoData.getITSTPCTRDTracksMCLabels().size(); ++iTrk) { + checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRD)); + } + for (size_t iTrk{0}; iTrk < recoData.getITSTPCTOFMatches().size(); ++iTrk) { + checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTOF)); + } + for (size_t iTrk{0}; iTrk < recoData.getITSTPCTRDTOFMatches().size(); ++iTrk) { + checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRDTOF)); + } + sw.Stop(); + LOGP(info, "doPullStudy: accepted {} pulls; rejected {} (in {:.2f} seconds)", nPulls, nPullsFail, sw.RealTime()); + mTrackCounter.print(); +} + +void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) +{ + LOGP(info, "Doing MC study"); + mTrackCounter.reset(); + TStopwatch sw; + sw.Start(); + int nTracks{0}; + + const int iSrc{0}; + const int nev = mcReader.getNEvents(iSrc); + std::unordered_map info; + + LOGP(info, "** Filling particle table ... "); + for (int iEve{0}; iEve < nev; ++iEve) { + const auto& mcTrks = mcReader.getTracks(iSrc, iEve); + for (int iTrk{0}; iTrk < mcTrks.size(); ++iTrk) { + const auto& mcTrk = mcTrks[iTrk]; + const auto pdg = mcTrk.GetPdgCode(); + if (o2::O2DatabasePDG::Instance()->GetParticle(pdg) == nullptr) { + continue; + } + const auto apdg = std::abs(pdg); + if (apdg != 11 && apdg != 211 && apdg != 321 && apdg != 2212) { + continue; + } + o2::MCCompLabel lbl(iTrk, iEve, iSrc); + auto& part = info[lbl]; + part.mcTrack = mcTrk; + } + } + LOGP(info, "** Creating particle/clusters correspondence ... "); + const auto& clusters = recoData.getITSClusters(); + const auto& clustersMCLCont = recoData.getITSClustersMCLabels(); + for (auto iCluster{0}; iCluster < clusters.size(); ++iCluster) { + auto labs = clustersMCLCont->getLabels(iCluster); + for (auto& lab : labs) { + if (!lab.isValid() || lab.getSourceID() != 0 || !lab.isCorrect()) { + continue; + } + int trackID = 0, evID = 0, srcID = 0; + bool fake = false; + lab.get(trackID, evID, srcID, fake); + auto& cluster = clusters[iCluster]; + auto layer = o2::its::GeometryTGeo::Instance()->getLayer(cluster.getSensorID()); + auto& part = info[{trackID, evID, srcID}]; + part.clusters |= (1 << layer); + if (fake) { + part.fakeClusters |= (1 << layer); + } + } + } + LOGP(info, "** Analysing tracks ... "); + auto accountLbl = [&](const globaltracking::RecoContainer::GlobalIDSet& contributorsGID, DetID::ID det) { + if (contributorsGID[det].isIndexSet()) { + const auto& lbl = recoData.getTrackMCLabel(contributorsGID[det]); + if (lbl.isValid()) { + o2::MCCompLabel iLbl(lbl.getTrackID(), lbl.getEventID(), lbl.getSourceID()); + if (info.contains(iLbl)) { + auto& part = info[iLbl]; + SETBIT(part.recoTracks, det); + if (lbl.isFake()) { + SETBIT(part.fakeTracks, det); + } + } + } + } + }; + auto creator = [&](const auto& trk, GTrackID trkID, float _t0, float terr) -> bool { + if constexpr (!isBarrelTrack()) { + return false; + } + if (!trkID.includesDet(GTrackID::ITS)) { + return false; + } + // general + auto contributorsGID = recoData.getSingleDetectorRefs(trkID); + if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS + return false; + } + const auto& gLbl = recoData.getTrackMCLabel(trkID); + if (!gLbl.isValid()) { + return false; + } + o2::MCCompLabel iLbl(gLbl.getTrackID(), gLbl.getEventID(), gLbl.getSourceID()); + if (!info.contains(iLbl)) { + return false; + } + auto& part = info[iLbl]; + part.recoTrack = recoData.getTrackParam(trkID); + + accountLbl(contributorsGID, DetID::ITS); + accountLbl(contributorsGID, DetID::TPC); + accountLbl(contributorsGID, DetID::TRD); + accountLbl(contributorsGID, DetID::TOF); + + ++nTracks; + return true; + }; + recoData.createTracksVariadic(creator); + + LOGP(info, "Streaming output to tree"); + for (const auto& [_, part] : info) { + (*mDBGOut) << "mc" + << "part=" << part + << "\n"; + } + + sw.Stop(); + LOGP(info, "doMCStudy: accounted {} MCParticles and {} tracks (in {:.2f} seconds)", info.size(), nTracks, sw.RealTime()); +} + +DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestIT3Clusters(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); + + return DataProcessorSpec{ + .name = "its3-track-study", + .inputs = dataRequest->inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, + .options = {}}; +} + +} // namespace o2::its3::study diff --git a/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx b/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx new file mode 100644 index 0000000000000..e0a0aea1c368a --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx @@ -0,0 +1,73 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITS3TrackingStudy/TrackingStudy.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, "ITS,TRD,TOF", {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::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,TRD,TOF,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS,TPC,TRD,TOF"); + + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + auto useMC = !configcontext.options().get("disable-mc"); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); + + specs.emplace_back(o2::its3::study::getTrackingStudySpec(srcTrc, srcCls, useMC)); + + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/RecoWorkflow.h b/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/RecoWorkflow.h index 1760aa1d850eb..010e1cd0a8127 100644 --- a/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/RecoWorkflow.h @@ -15,17 +15,15 @@ /// @file RecoWorkflow.h #include "Framework/WorkflowSpec.h" - -#include "GPUO2Interface.h" -#include "GPUReconstruction.h" -#include "GPUChainITS.h" +#include "ITStracking/Configuration.h" +#include "GPUDataTypesConfig.h" namespace o2::its3::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, - const std::string& trmode, - o2::gpu::GPUDataTypes::DeviceType dtype, + its::TrackingMode::Type trmode, + o2::gpu::gpudatatypes::DeviceType dtype, bool useGPUWorkflow, bool upstreamDigits, bool upstreamClusters, diff --git a/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/TrackerSpec.h b/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/TrackerSpec.h index f5c1d7bf0e947..66d58a5f5a92c 100644 --- a/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/TrackerSpec.h +++ b/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/TrackerSpec.h @@ -23,11 +23,17 @@ #include "ITS3Reconstruction/TrackingInterface.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TStopwatch.h" +namespace o2::gpu +{ +class GPUReconstruction; +class GPUChainITS; +} // namespace o2::gpu + namespace o2::its3 { @@ -37,9 +43,9 @@ class TrackerDPL : public framework::Task TrackerDPL(std::shared_ptr gr, bool isMC, int trgType, - const its::TrackingMode& trMode = its::TrackingMode::Unset, + its::TrackingMode::Type trmode = its::TrackingMode::Unset, const bool overrBeamEst = false, - gpu::GPUDataTypes::DeviceType dType = gpu::GPUDataTypes::DeviceType::CPU); + gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; TrackerDPL(const TrackerDPL&) = delete; TrackerDPL(TrackerDPL&&) = delete; @@ -63,7 +69,7 @@ class TrackerDPL : public framework::Task /// create a processor spec /// run ITS CA tracker -framework::DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int useTrig, const std::string& trMode, const bool overrBeamEst = false, gpu::GPUDataTypes::DeviceType dType = gpu::GPUDataTypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int useTrig, its::TrackingMode::Type trMode, const bool overrBeamEst = false, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 947e53f80ddf1..60fe4fabfe481 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -12,7 +12,7 @@ #include "ITS3Workflow/RecoWorkflow.h" #include "ITS3Workflow/ClustererSpec.h" #include "ITS3Workflow/TrackerSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITS3Workflow/DigitReaderSpec.h" #include "GPUWorkflow/GPUWorkflowSpec.h" @@ -26,7 +26,7 @@ static std::shared_ptr gTask; namespace o2::its3::reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, const std::string& trmode, o2::gpu::GPUDataTypes::DeviceType dtype, bool useGPUWorkflow, +framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, o2::gpu::gpudatatypes::DeviceType dtype, bool useGPUWorkflow, bool upstreamDigits, bool upstreamClusters, bool disableRootOutput, bool useGeom, int useTrig, bool overrideBeamPosition) { framework::WorkflowSpec specs; @@ -40,10 +40,10 @@ framework::WorkflowSpec getWorkflow(bool useMC, const std::string& trmode, o2::g } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } - if (trmode != "off") { + if (trmode != its::TrackingMode::Off) { if (useGPUWorkflow) { o2::gpu::GPURecoWorkflowSpec::Config cfg; cfg.runITSTracking = true; diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index 5b710a3d11fef..0326c12f804e0 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx @@ -29,18 +29,11 @@ #include "CommonDataFormat/IRFrame.h" #include "DataFormatsTRD/TriggerRecord.h" #include "ITS3Reconstruction/IOUtils.h" -#include "ITSReconstruction/FastMultEstConfig.h" #include "ITS3Base/SpecsV2.h" namespace o2 { using namespace framework; -using its::FastMultEstConfig; -using its::TimeFrame; -using its::Tracker; -using its::TrackingParameters; -using its::TrackITSExt; -using its::Vertexer; namespace its3 { @@ -49,9 +42,9 @@ using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, int trgType, - const its::TrackingMode& trMode, + its::TrackingMode::Type trMode, const bool overrBeamEst, - o2::gpu::GPUDataTypes::DeviceType dType) : mGGCCDBRequest(gr), + o2::gpu::gpudatatypes::DeviceType dType) : mGGCCDBRequest(gr), mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)}, mITS3TrackingInterface{isMC, trgType, overrBeamEst} { @@ -95,7 +88,7 @@ void TrackerDPL::endOfStream(EndOfStreamContext& ec) LOGF(info, "ITS3 CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, const std::string& trModeS, const bool overrBeamEst, o2::gpu::GPUDataTypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, its::TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) { std::vector inputs; inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); @@ -145,10 +138,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, const st "its3-tracker", inputs, outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC, trgType, - trModeS == "sync" ? o2::its::TrackingMode::Sync : trModeS == "async" ? o2::its::TrackingMode::Async - : o2::its::TrackingMode::Cosmics, - overrBeamEst, dType)}, + AlgorithmSpec{adaptFromTask(ggRequest, useMC, trgType, trMode, overrBeamEst, dType)}, Options{}}; } diff --git a/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx index 2f0eda73742cb..018ad807a974a 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx @@ -63,13 +63,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto beamPosOVerride = configcontext.options().get("ccdb-meanvertex-seed"); auto trmode = configcontext.options().get("tracking-mode"); auto selTrig = configcontext.options().get("select-with-triggers"); - auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); + auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); auto useGeom = configcontext.options().get("use-full-geometry"); auto useGPUWfx = configcontext.options().get("use-gpu-workflow"); - std::transform(trmode.begin(), trmode.end(), trmode.begin(), [](unsigned char c) { return std::tolower(c); }); o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); int trType = 0; @@ -82,7 +81,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOG(fatal) << "Unknown trigger type requested for events prescaling: " << selTrig; } } - auto wf = o2::its3::reco_workflow::getWorkflow(useMC, trmode, gpuDevice, useGPUWfx, extDigits, extClusters, disableRootOutput, useGeom, trType, beamPosOVerride); + auto wf = o2::its3::reco_workflow::getWorkflow(useMC, o2::its::TrackingMode::fromString(trmode), gpuDevice, useGPUWfx, extDigits, extClusters, disableRootOutput, useGeom, trType, beamPosOVerride); // 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/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h b/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h deleted file mode 100644 index cd1742e24fa72..0000000000000 --- a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h +++ /dev/null @@ -1,1297 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FwdDCAFitterN.h -/// \brief Defintions for N-prongs secondary vertex fit -/// \author ruben.shahoyan@cern.ch, adapted from central barrel to fwd rapidities by Rita Sadek, rita.sadek@cern.ch -/// For the formulae derivation see /afs/cern.ch/user/s/shahoian/public/O2/DCAFitter/DCAFitterN.pdf - -#ifndef _ALICEO2_DCA_FWDFITTERN_ -#define _ALICEO2_DCA_FWDFITTERN_ -#include -#include "MathUtils/Cartesian.h" -#include "ReconstructionDataFormats/TrackFwd.h" -#include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" -#include -#include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GeometryManager.h" - -namespace o2 -{ -namespace vertexing -{ - -///__________________________________________________________________________________ -///< Fwd Inverse cov matrix (augmented by a dummy Z error) of the point defined by the track -struct FwdTrackCovI { - float sxx, syy, sxy, szz; - - FwdTrackCovI(const o2::track::TrackParCovFwd& trc, float zerrFactor = 1.) { set(trc, zerrFactor); } - FwdTrackCovI() = default; - void set(const o2::track::TrackParCovFwd& trc, float zerrFactor = 1) - { - float cxx = trc.getSigma2X(), cyy = trc.getSigma2Y(), cxy = trc.getSigmaXY(), czz = cyy * zerrFactor; - float detXY = cxx * cyy - cxy * cxy; - if (detXY > 0.) { - auto detXYI = 1. / detXY; - sxx = cyy * detXYI; - syy = cxx * detXYI; - sxy = -cxy * detXYI; - szz = 1. / czz; - } else { - throw std::runtime_error("invalid track covariance"); - } - } -}; - -///__________________________________________________________________________ -///< Fwd derivative (up to 2) of the TrackParam position over its running param Z -struct FwdTrackDeriv { - float dxdz, dydz, d2xdz2, d2ydz2; - FwdTrackDeriv() = default; - FwdTrackDeriv(const o2::track::TrackParFwd& trc, float bz) { set(trc, bz); } - void set(const o2::track::TrackParFwd& trc, float bz) - { - float snp = trc.getSnp(), csp = std::sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz), tgl = trc.getTanl(), tglI = 1. / tgl; - if (crv2c == 0.) { - crv2c = (trc.getCharge()) * 0.3 * bz * (-1e-3); - } - - dxdz = csp * tglI; - dydz = snp * tglI; - d2xdz2 = crv2c * snp * tglI * tglI; - d2ydz2 = -crv2c * csp * tglI * tglI; - } -}; - -template -class FwdDCAFitterN -{ - static constexpr double NMin = 2; - static constexpr double NMax = 4; - static constexpr double NInv = 1. / N; - static constexpr int MAXHYP = 2; - static constexpr float ZerrFactor = 5.; // factor for conversion of track covXX to dummy covZZ - using Track = o2::track::TrackParCovFwd; - 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 SMatrix55 = ROOT::Math::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. - using ArrTrCoef = std::array; // container of TrackCoefVtx coefficients at single vertex cand. - using ArrTrDer = std::array; // container of Track 1st and 2nd derivative over their Z param - using ArrTrPos = std::array; // container of Track positions - - public: - static constexpr int getNProngs() { return N; } - - FwdDCAFitterN() = default; - FwdDCAFitterN(float bz, bool useAbsDCA, bool prop2DCA) : mBz(bz), mUseAbsDCA(useAbsDCA), mPropagateToPCA(prop2DCA) - { - static_assert(N >= NMin && N <= NMax, "N prongs outside of allowed range"); - } - - //========================================================================= - ///< 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 - { - const auto& vd = mPCA[mOrder[cand]]; - return std::array{float(vd[0]), float(vd[1]), float(vd[2])}; - } - - ///< return Chi2 at PCA candidate (no check for its validity) - float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; } - - ///< prepare copies of tracks at the V0 candidate (no check for the candidate validity) - /// must be called before getTrack(i,cand) query - bool FwdpropagateTracksToVertex(int cand = 0); - - ///< check if propagation of tracks to candidate vertex was done - bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } - - ///< track param propagated to V0 candidate (no check for the candidate validity) - /// propagateTracksToVertex must be called in advance - Track& getTrack(int i, int cand = 0) - { - if (!mTrPropDone[mOrder[cand]]) { - throw std::runtime_error("propagateTracksToVertex was not called yet"); - } - return mCandTr[mOrder[cand]][i]; - } - - ///< calculate on the fly track param (no cov mat) at candidate - o2::track::TrackParFwd FwdgetTrackParamAtPCA(int i, int cand = 0) const; - - 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))}; - } - - const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; } - - ///< 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 = 60) { mMaxIter = n > 2 ? n : 2; } - void setMaxR(float r = 200.) { mMaxR2 = r * r; } - void setMaxDXIni(float d = 4.) { mMaxDXIni = d; } - 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 setMatLUT(const o2::base::MatLayerCylSet* m) - { - mMatLUT = m; - mUseMatBudget = true; - } - void setTGeoMat(bool v = true) { mTGeoFallBackAllowed = v; } - void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } - - int getNCandidates() const { return mCurHyp; } - int getMaxIter() const { return mMaxIter; } - float getMaxR() const { return std::sqrt(mMaxR2); } - float getMaxDXIni() const { return mMaxDXIni; } - float getMaxChi2() const { return mMaxChi2; } - float getMinParamChange() const { return mMinParamChange; } - float getBz() const { return mBz; } - double getK(double b) const { return std::abs(o2::constants::math::B2C * b); } - double getHz(double b) const { return std::copysign(1, b); } - - float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } - bool getUseAbsDCA() const { return mUseAbsDCA; } - bool getPropagateToPCA() const { return mPropagateToPCA; } - - template - int process(const Tr&... args); - void print() const; - - protected: - bool FwdcalcPCACoefs(); - bool FwdcalcInverseWeight(); - void FwdcalcResidDerivatives(); - void FwdcalcResidDerivativesNoErr(); - void FwdcalcChi2Derivatives(); - void FwdcalcChi2DerivativesNoErr(); - void FwdcalcPCA(); - void FwdcalcPCANoErr(); - void FwdcalcTrackResiduals(); - void calcTrackDerivatives(); - float findZatXY(int cand = 0); - void findZatXY_mid(int cand = 0); - void findZatXY_lineApprox(int cand = 0); - void findZatXY_quad(int cand = 0); - void findZatXY_linear(int cand = 0); - double FwdcalcChi2() const; - double FwdcalcChi2NoErr() const; - bool FwdcorrectTracks(const VecND& corrZ); - bool minimizeChi2(); - bool minimizeChi2NoErr(); - bool roughDXCut() const; - bool closerToAlternative() const; - static double getAbsMax(const VecND& v); - bool propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const; - - ///< track param positions at V0 candidate (no check for the candidate validity) - const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } - - ///< track Z-param at V0 candidate (no check for the candidate validity) - float getTrackZ(int i, int cand = 0) const { return getTrackPos(i, cand)[2]; } - - MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame - // no rotation for fwd: mat=I - { - MatStd3D mat; - mat(0, 0) = 1; - mat(1, 1) = 1; - mat(2, 2) = 1; - return mat; - } - - MatSym3D getTrackCovMatrix(int i, int cand = 0) const // generate covariance matrix of track position, adding fake Z error - { - const auto& trc = mCandTr[mOrder[cand]][i]; - MatSym3D mat; - mat(0, 0) = trc.getSigma2X(); - mat(1, 1) = trc.getSigma2Y(); - mat(1, 0) = trc.getSigmaXY(); - mat(2, 2) = trc.getSigma2Y() * ZerrFactor; - return mat; - } - - void assign(int) {} - template - void assign(int i, const T& t, const Tr&... args) - { - static_assert(std::is_convertible(), "Wrong track type"); - mOrigTrPtr[i] = &t; - assign(i + 1, args...); - } - - void clear() - { - mCurHyp = 0; - mAllowAltPreference = true; - } - - static void setTrackPos(Vec3D& pnt, const Track& tr) - { - pnt[0] = tr.getX(); - pnt[1] = tr.getY(); - pnt[2] = tr.getZ(); - } - - private: - // vectors of 1st derivatives of track local residuals over Z parameters - std::array, N> mDResidDz; - // vectors of 1nd derivatives of track local residuals over Z parameters - std::array, N> mD2ResidDz2; - VecND mDChi2Dz; // 1st derivatives of chi2 over tracks Z params - MatSymND mD2Chi2Dz2; // 2nd derivatives of chi2 over tracks Z params (symmetric matrix) - - std::array mOrigTrPtr; - std::array mTrAux; // Aux track info for each track at each cand. vertex - CrossInfo mCrossings; // info on track crossing - - std::array mTrcEInv; // errors for each track at each cand. vertex - std::array mCandTr; // tracks at each cond. vertex (Note: Errors are at seed XY point) - std::array mTrCFVT; // TrackCoefVtx for each track at each cand. vertex - std::array mTrDer; // Track derivativse - std::array mTrPos; // Track positions - std::array mTrRes; // Track residuals - std::array mPCA; // PCA for each vertex candidate - std::array mChi2 = {0}; // Chi2 at PCA candidate - std::array mNIters; // number of iterations for each seed - std::array mTrPropDone; // Flag that the tracks are fully propagated to PCA - MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T - std::array mOrder{0}; - int mCurHyp = 0; - int mCrossIDCur = 0; - int mCrossIDAlt = -1; - bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one - bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 - bool mPropagateToPCA = true; // create tracks version propagated to PCA - bool mUseMatBudget = false; // include MCS effects in track propagation - bool mTGeoFallBackAllowed = true; // use TGeo for precise estimate of mat. budget - int mMaxIter = 60; // max number of iterations - float mBz = 0; // bz field, to be set by user - float mMaxR2 = 200. * 200.; // reject PCA's above this radius - float mMaxDXIni = 4.; // reject (if>0) PCA candidate if tracks DZ exceeds threshold - float mMinParamChange = 1e-5; // stop iterations if largest change of any X is smaller than this - float mMinRelChi2Change = 0.98; // stop iterations is chi2/chi2old > this - float mMaxChi2 = 100; // abs cut on chi2 or abs distance - float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold - const o2::base::MatLayerCylSet* mMatLUT = nullptr; // use to compute material budget to include MCS effects - - ClassDefNV(FwdDCAFitterN, 1); -}; - -///_________________________________________________________________________ -template -template -int FwdDCAFitterN::process(const Tr&... args) -{ - - static_assert(sizeof...(args) == N, "incorrect number of input tracks"); - assign(0, args...); - clear(); - - for (int i = 0; i < N; i++) { - mTrAux[i].set(*mOrigTrPtr[i], mBz); - } - - if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1])) { // even for N>2 it should be enough to test just 1 loop - return 0; // no crossing - } - - if (mCrossings.nDCA == MAXHYP) { // if there are 2 candidates - auto dst2 = (mCrossings.xDCA[0] - mCrossings.xDCA[1]) * (mCrossings.xDCA[0] - mCrossings.xDCA[1]) + - (mCrossings.yDCA[0] - mCrossings.yDCA[1]) * (mCrossings.yDCA[0] - mCrossings.yDCA[1]); - - if (dst2 < mMaxDist2ToMergeSeeds) { - mCrossings.nDCA = 1; - mCrossings.xDCA[0] = 0.5 * (mCrossings.xDCA[0] + mCrossings.xDCA[1]); - mCrossings.yDCA[0] = 0.5 * (mCrossings.yDCA[0] + mCrossings.yDCA[1]); - } - } - - // check all crossings - 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) { - 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.; - - findZatXY_mid(mCurHyp); - - if (mUseAbsDCA ? minimizeChi2NoErr() : minimizeChi2()) { - mOrder[mCurHyp] = mCurHyp; - if (mPropagateToPCA && !FwdpropagateTracksToVertex(mCurHyp)) { - continue; - } - mCurHyp++; - } - } - - 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]); - } - } - } - - return mCurHyp; -} - -//__________________________________________________________________________ -template -bool FwdDCAFitterN::FwdcalcPCACoefs() -{ - //< calculate Ti matrices for global vertex decomposition to V = sum_{0 -bool FwdDCAFitterN::FwdcalcInverseWeight() -{ - //< calculate [sum_{0 -void FwdDCAFitterN::FwdcalcResidDerivatives() -{ - //< calculate matrix of derivatives for weighted chi2: residual i vs parameter Z of track j - MatStd3D matMT; - for (int i = N; i--;) { // residual being differentiated - // const auto& taux = mTrAux[i]; - for (int j = N; j--;) { // track over which we differentiate - const auto& matT = mTrCFVT[mCurHyp][j]; // coefficient matrix for track J - const auto& trDz = mTrDer[mCurHyp][j]; // track point derivs over track Z param - auto& dr1 = mDResidDz[i][j]; - auto& dr2 = mD2ResidDz2[i][j]; - // calculate M_i^transverse * T_j , M_i^transverse=I -> MT=T - matMT[0][0] = matT[0][0]; - matMT[0][1] = matT[0][1]; - matMT[0][2] = matT[0][2]; - matMT[1][0] = matT[1][0]; - matMT[1][1] = matT[1][1]; - matMT[1][2] = matT[1][2]; - matMT[2][0] = matT[2][0]; - matMT[2][1] = matT[2][1]; - matMT[2][2] = matT[2][2]; - - // calculate DResid_i/Dz_j = (delta_ij - M_i^tr * T_j) * DTrack_k/Dz_k - dr1[0] = -(matMT[0][0] * trDz.dxdz + matMT[0][1] * trDz.dydz + matMT[0][2]); - dr1[1] = -(matMT[1][0] * trDz.dxdz + matMT[1][1] * trDz.dydz + matMT[1][2]); - dr1[2] = -(matMT[2][0] * trDz.dxdz + matMT[2][1] * trDz.dydz + matMT[2][2]); - - // calculate D2Resid_I/(Dz_J Dz_K) = (delta_ijk - M_i^tr * T_j * delta_jk) * D2Track_k/dz_k^2 - dr2[0] = -(matMT[0][1] * trDz.d2ydz2 + matMT[0][0] * trDz.d2xdz2); - dr2[1] = -(matMT[1][1] * trDz.d2ydz2 + matMT[1][0] * trDz.d2xdz2); - dr2[2] = -(matMT[2][1] * trDz.d2ydz2 + matMT[2][0] * trDz.d2xdz2); - - if (i == j) { - dr1[0] += trDz.dxdz; - dr1[1] += trDz.dydz; - dr1[2] += 1.; - - dr2[0] += trDz.d2xdz2; - dr2[1] += trDz.d2ydz2; - } - } // track over which we differentiate - } // residual being differentiated -} - -//__________________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcResidDerivativesNoErr() -{ - //< calculate matrix of derivatives for absolute distance chi2: residual i vs parameter Z of track j - constexpr double NInv1 = 1. - NInv; // profit from Rii = I/Ninv - for (int i = N; i--;) { // residual being differentiated - const auto& trDzi = mTrDer[mCurHyp][i]; // track point derivs over track Z param - auto& dr1ii = mDResidDz[i][i]; - auto& dr2ii = mD2ResidDz2[i][i]; - - dr1ii[0] = NInv1 * trDzi.dxdz; - dr1ii[1] = NInv1 * trDzi.dydz; - dr1ii[2] = NInv1; - - dr2ii[0] = NInv1 * trDzi.d2xdz2; - dr2ii[1] = NInv1 * trDzi.d2ydz2; - dr2ii[2] = 0; - - for (int j = i; j--;) { // track over which we differentiate - auto& dr1ij = mDResidDz[i][j]; - auto& dr1ji = mDResidDz[j][i]; - const auto& trDzj = mTrDer[mCurHyp][j]; // track point derivs over track Z param - - // calculate DResid_i/Dz_j = (delta_ij - R_ij) * DTrack_j/Dz_j for j -void FwdDCAFitterN::FwdcalcChi2Derivatives() -{ - //< calculate 1st and 2nd derivatives of wighted DCA (chi2) over track parameters Z - std::array, N> covIDrDz; // tempory vectors of covI_j * dres_j/dz_i - - // chi2 1st derivative - for (int i = N; i--;) { - auto& dchi1 = mDChi2Dz[i]; // DChi2/Dz_i = sum_j { res_j * covI_j * Dres_j/Dz_i } - dchi1 = 0; - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& covI = mTrcEInv[mCurHyp][j]; // inverse cov matrix of track j - const auto& dr1 = mDResidDz[j][i]; // vector of j-th residuals 1st derivative over Z param of track i - auto& cidr = covIDrDz[i][j]; // vector covI_j * dres_j/dz_i, save for 2nd derivative calculation - cidr[0] = covI.sxx * dr1[0] + covI.sxy * dr1[1]; - cidr[1] = covI.sxy * dr1[0] + covI.syy * dr1[1]; - cidr[2] = covI.szz * dr1[2]; - - dchi1 += ROOT::Math::Dot(res, cidr); - } - } - - // chi2 2nd derivative - for (int i = N; i--;) { - for (int j = i + 1; j--;) { // symmetric matrix - auto& dchi2 = mD2Chi2Dz2[i][j]; // D2Chi2/Dz_i/Dz_j = sum_k { Dres_k/Dz_j * covI_k * Dres_k/Dz_i + res_k * covI_k * D2res_k/Dz_i/Dz_j } - dchi2 = 0; - for (int k = N; k--;) { - const auto& dr1j = mDResidDz[k][j]; // vector of k-th residuals 1st derivative over Z param of track j - const auto& cidrkj = covIDrDz[i][k]; // vector covI_k * dres_k/dz_i - dchi2 += ROOT::Math::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 - const auto& dr2ij = mD2ResidDz2[k][j]; // vector of k-th residuals 2nd derivative over Z params of track j - dchi2 += res[0] * (covI.sxx * dr2ij[0] + covI.sxy * dr2ij[1]) + res[1] * (covI.sxy * dr2ij[0] + covI.syy * dr2ij[1]) + res[2] * covI.szz * dr2ij[2]; - } - } - } - } -} - -//__________________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcChi2DerivativesNoErr() -{ - //< calculate 1st and 2nd derivatives of abs DCA (chi2) over track parameters Z - for (int i = N; i--;) { - auto& dchi1 = mDChi2Dz[i]; // DChi2/Dz_i = sum_j { res_j * Dres_j/Dz_i } - dchi1 = 0; // chi2 1st derivative - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& dr1 = mDResidDz[j][i]; // vector of j-th residuals 1st derivative over Z param of track i - dchi1 += ROOT::Math::Dot(res, dr1); - if (i >= j) { // symmetrix matrix - // chi2 2nd derivative - auto& dchi2 = mD2Chi2Dz2[i][j]; // D2Chi2/Dz_i/Dz_j = sum_k { Dres_k/Dz_j * covI_k * Dres_k/Dz_i + res_k * covI_k * D2res_k/Dz_i/Dz_j } - dchi2 = ROOT::Math::Dot(mTrRes[mCurHyp][i], mD2ResidDz2[i][j]); - for (int k = N; k--;) { - dchi2 += ROOT::Math::Dot(mDResidDz[k][i], mDResidDz[k][j]); - } - } - } - } -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcPCA() -{ - // calculate point of closest approach for N prongs - // calculating V = sum (Ti*Pi) - mPCA[mCurHyp] = mTrCFVT[mCurHyp][N - 1] * mTrPos[mCurHyp][N - 1]; - for (int i = N - 1; i--;) { - mPCA[mCurHyp] += mTrCFVT[mCurHyp][i] * mTrPos[mCurHyp][i]; - } -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcPCANoErr() -{ - // calculate point of closest approach for N prongs w/o errors - auto& pca = mPCA[mCurHyp]; - - pca[0] = mTrPos[mCurHyp][N - 1][0]; - pca[1] = mTrPos[mCurHyp][N - 1][1]; - pca[2] = mTrPos[mCurHyp][N - 1][2]; - - for (int i = N - 1; i--;) { - pca[0] += mTrPos[mCurHyp][i][0]; - pca[1] += mTrPos[mCurHyp][i][1]; - pca[2] += mTrPos[mCurHyp][i][2]; - } - pca[0] *= NInv; - pca[1] *= NInv; - pca[2] *= NInv; -} - -//___________________________________________________________________ -template -ROOT::Math::SMatrix> FwdDCAFitterN::calcPCACovMatrix(int cand) const -{ - // calculate covariance matrix for the point of closest approach - MatSym3D covm; - for (int i = N; i--;) { - covm += ROOT::Math::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); - } - return covm; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcTrackResiduals() -{ - // calculate residuals, res = Pi - V - Vec3D vtxLoc; - for (int i = N; i--;) { - mTrRes[mCurHyp][i] = mTrPos[mCurHyp][i]; - vtxLoc = mPCA[mCurHyp]; - mTrRes[mCurHyp][i] -= vtxLoc; - } -} - -//___________________________________________________________________ -template -inline void FwdDCAFitterN::calcTrackDerivatives() -{ - // calculate track derivatives over Z param - for (int i = N; i--;) { - mTrDer[mCurHyp][i].set(mCandTr[mCurHyp][i], mBz); - } -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::FwdcalcChi2() const -{ - // calculate current chi2 - double chi2 = 0; - for (int i = N; i--;) { - const auto& res = mTrRes[mCurHyp][i]; - const auto& covI = mTrcEInv[mCurHyp][i]; - chi2 += res[0] * res[0] * covI.sxx + res[1] * res[1] * covI.syy + res[2] * res[2] * covI.szz + 2. * res[0] * res[1] * covI.sxy; - } - return chi2; -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::FwdcalcChi2NoErr() const -{ - // calculate current chi2 of abs. distance minimization - double chi2 = 0; - for (int i = N; i--;) { - const auto& res = mTrRes[mCurHyp][i]; - chi2 += res[0] * res[0] + res[1] * res[1] + res[2] * res[2]; - } - return chi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::FwdcorrectTracks(const VecND& corrZ) -{ - // propagate tracks to updated Z - for (int i = N; i--;) { - const auto& trDer = mTrDer[mCurHyp][i]; - auto dz2h = 0.5 * corrZ[i] * corrZ[i]; - mTrPos[mCurHyp][i][0] -= trDer.dxdz * corrZ[i] - dz2h * trDer.d2xdz2; - mTrPos[mCurHyp][i][1] -= trDer.dydz * corrZ[i] - dz2h * trDer.d2ydz2; - mTrPos[mCurHyp][i][2] -= corrZ[i]; - } - - return true; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::FwdpropagateTracksToVertex(int icand) -{ - // propagate on z axis to vertex - int ord = mOrder[icand]; - if (mTrPropDone[ord]) { - return true; - } - const Vec3D& pca = mPCA[ord]; - std::array covMatrixPCA = calcPCACovMatrixFlat(ord); - std::array cov = {covMatrixPCA[0], covMatrixPCA[2]}; - for (int i = N; i--;) { - mCandTr[ord][i] = *mOrigTrPtr[i]; // fetch the track again, as mCandTr might have been propagated w/o errors - auto& trc = mCandTr[ord][i]; - const std::array p = {(float)pca[0], (float)pca[1], (float)pca[2]}; - if (!propagateToVtx(trc, p, cov)) { - return false; - } - } - - mTrPropDone[ord] = true; - return true; -} - -//___________________________________________________________________ -template -float FwdDCAFitterN::findZatXY(int mCurHyp) // Between 2 tracks -{ - - double step = 0.001; // initial step - double startPoint = 20.; // first MFT disk - - double z[2] = {startPoint, startPoint}; - double newX[2], newY[2]; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double dstXY[2][3] = {{999., 999., 999.}, {999., 999., 999.}}; - - double Z[2]; - double finalZ[2]; - - double newDstXY; - - for (int i = 0; i < 2; i++) { - - while (z[i] > -10) { - - mCandTr[mCurHyp][i].propagateParamToZquadratic(z[i], mBz); - newX[i] = mCandTr[mCurHyp][i].getX(); - newY[i] = mCandTr[mCurHyp][i].getY(); - - newDstXY = std::sqrt((newX[i] - X) * (newX[i] - X) + - (newY[i] - Y) * (newY[i] - Y)); - - // Update points - dstXY[i][0] = dstXY[i][1]; - dstXY[i][1] = dstXY[i][2]; - dstXY[i][2] = newDstXY; - - if (dstXY[i][2] > dstXY[i][1] && dstXY[i][1] < dstXY[i][0]) { - finalZ[i] = z[i] + step; - break; - } - - z[i] -= step; - } - } - - float rez = 0.5 * (finalZ[0] + finalZ[1]); - return rez; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_mid(int mCurHyp) -{ - // look into dXY of T0 - T1 between 2 points(0,40cm); the one with the highest dXY is moved to mid - - double startPoint = -40.; - double endPoint = 50.; - double midPoint = 0.5 * (startPoint + endPoint); - - double z[2][2] = {{startPoint, endPoint}, {startPoint, endPoint}}; // z for tracks 0/1 on starting poing and endpoint - - double DeltaZ = std::abs(endPoint - startPoint); - - double newX[2][2]; - double newY[2][2]; - - double epsilon = 0.0001; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double finalZ; - - double dstXY[2]; // 0 -> distance btwn both tracks at startPoint - - while (DeltaZ > epsilon) { - - midPoint = 0.5 * (startPoint + endPoint); - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateParamToZquadratic(startPoint, mBz); - newX[i][0] = mCandTr[mCurHyp][i].getX(); - newY[i][0] = mCandTr[mCurHyp][i].getY(); - - mCandTr[mCurHyp][i].propagateParamToZquadratic(endPoint, mBz); - newX[i][1] = mCandTr[mCurHyp][i].getX(); - newY[i][1] = mCandTr[mCurHyp][i].getY(); - } - - dstXY[0] = (newX[0][0] - newX[1][0]) * (newX[0][0] - newX[1][0]) + - (newY[0][0] - newY[1][0]) * (newY[0][0] - newY[1][0]); - - dstXY[1] = (newX[0][1] - newX[1][1]) * (newX[0][1] - newX[1][1]) + - (newY[0][1] - newY[1][1]) * (newY[0][1] - newY[1][1]); - - DeltaZ = std::abs(endPoint - startPoint); - - if (DeltaZ < epsilon) { - finalZ = 0.5 * (startPoint + endPoint); - break; - } - - // chose new start and end Point according to the smallest D_XY - if (dstXY[1] > dstXY[0]) { - endPoint = midPoint; - } else { - startPoint = midPoint; - } - } - - mPCA[mCurHyp][2] = finalZ; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_lineApprox(int mCurHyp) -{ - // approx method: z=(b-b')/(a'-a) -> tracks to lines with y0,1=az0,1+b for each track (in YZ and XZ plane) - - double startPoint = 1.; - double endPoint = 50.; // first disk - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double y[2][2]; // Y00: y track 0 at point 0; Y01: y track 0 at point 1 - double z[2][2]; - double x[2][2]; - - double aYZ[2]; - double bYZ[2]; - - double aXZ[2]; - double bXZ[2]; - - double finalZ; - - // find points of the tracks = 2 straight lines - for (int i = 0; i < 2; i++) { - - mCandTr[mCurHyp][i].propagateToZquadratic(startPoint, mBz); - // mCandTr[mCurHyp][i].propagateToZlinear(startPoint); - z[i][0] = startPoint; - y[i][0] = mCandTr[mCurHyp][i].getY(); - x[i][0] = mCandTr[mCurHyp][i].getX(); - - mCandTr[mCurHyp][i].propagateToZquadratic(endPoint, mBz); - // mCandTr[mCurHyp][i].propagateToZlinear(endPoint); - z[i][1] = endPoint; - y[i][1] = mCandTr[mCurHyp][i].getY(); - x[i][1] = mCandTr[mCurHyp][i].getX(); - - bYZ[i] = (y[i][1] - y[i][0] * z[i][1] / z[i][0]) / (1 - z[i][1] / z[i][0]); - aYZ[i] = (y[i][0] - bYZ[i]) / z[i][0]; - - bXZ[i] = (x[i][1] - x[i][0] * z[i][1] / z[i][0]) / (1 - z[i][1] / z[i][0]); - aXZ[i] = (x[i][0] - bXZ[i]) / z[i][0]; - } - - // z seed: equ. for intersection of these lines - finalZ = 0.5 * ((bYZ[0] - bYZ[1]) / (aYZ[1] - aYZ[0]) + (bXZ[0] - bXZ[1]) / (aXZ[1] - aXZ[0])); - - mPCA[mCurHyp][2] = finalZ; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_quad(int mCurHyp) -{ - double startPoint = 0.; - double endPoint = 40.; // first disk - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double x[2]; - double y[2]; - double sinPhi0[2]; - double cosPhi0[2]; - double tanL0[2]; - double qpt0[2]; - - double k[2]; // B2C *abs(mBz) - double Hz[2]; // mBz/abs(mBz) - - double Ax[2], Bx[2], Cx[2]; - double Ay[2], By[2], Cy[2]; - - double deltaX[2], deltaY[2]; - - bool posX[2], nulX[2], negX[2]; - double z1X[2], z2X[2], z12X[2]; - - bool posY[2], nulY[2], negY[2]; - double z1Y[2], z2Y[2], z12Y[2]; - - double finalZ[2]; - - // find all variables for 2 tracks at z0 = startPoint - // set A, B, C variables for x/y equation for 2 tracks - // calculate Deltax/y for both and roots - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateToZquadratic(startPoint, mBz); - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - sinPhi0[i] = mCandTr[mCurHyp][i].getSnp(); - cosPhi0[i] = std::sqrt((1. - sinPhi0[i]) * (1. + sinPhi0[i])); - tanL0[i] = mCandTr[mCurHyp][i].getTanl(); - qpt0[i] = mCandTr[mCurHyp][i].getInvQPt(); - k[i] = getK(mBz); - Hz[i] = getHz(mBz); - - Ax[i] = qpt0[i] * Hz[i] * k[i] * sinPhi0[i] / (2 * tanL0[i] * tanL0[i]); - Bx[i] = cosPhi0[i] / tanL0[i]; - Cx[i] = x[i] - X; - - Ay[i] = -qpt0[i] * Hz[i] * k[i] * cosPhi0[i] / (2 * tanL0[i] * tanL0[i]); - By[i] = sinPhi0[i] / tanL0[i]; - Cy[i] = y[i] - Y; // - - deltaX[i] = Bx[i] * Bx[i] - 4 * Ax[i] * Cx[i]; - deltaY[i] = By[i] * By[i] - 4 * Ay[i] * Cy[i]; - - if (deltaX[i] > 0) { - posX[i] = true; - z1X[i] = (-Bx[i] - std::sqrt(deltaX[i])) / (2 * Ax[i]); - z2X[i] = (-Bx[i] + std::sqrt(deltaX[i])) / (2 * Ax[i]); - } else if (deltaX[i] == 0) { - nulX[i] = true; - z12X[i] = -Bx[i] / (2 * Ax[i]); - } else { - negX[i] = true; - z12X[i] = 0; - } // discard - - if (deltaY[i] > 0) { - posY[i] = true; - z1Y[i] = (-By[i] - std::sqrt(deltaY[i])) / (2 * Ay[i]); - z2Y[i] = (-By[i] + std::sqrt(deltaY[i])) / (2 * Ay[i]); - } else if (deltaX[i] == 0) { - nulY[i] = true; - z12Y[i] = -By[i] / (2 * Ay[i]); - } else { - negY[i] = true; - z12Y[i] = 0; - } - - // find the z located in an acceptable interval - if (posX[i]) { - if (z1X[i] < endPoint && z1X[i] > startPoint) { - z12X[i] = z1X[i]; - } else { - z12X[i] = z2X[i]; - } - } - - if (posY[i]) { - if (z1Y[i] < endPoint && z1Y[i] > startPoint) { - z12Y[i] = z1Y[i]; - } else { - z12Y[i] = z2Y[i]; - } - } - - finalZ[i] = 0.5 * (z12X[i] + z12Y[i]); - } - - mPCA[mCurHyp][2] = 0.5 * (finalZ[0] + finalZ[1]); -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_linear(int mCurHyp) -{ - - double startPoint = 0.; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double x[2]; - double y[2]; - double sinPhi0[2]; - double cosPhi0[2]; - double tanL0[2]; - - double Ax[2], Bx[2]; - double Ay[2], By[2]; - - double z12X[2]; - double z12Y[2]; - - double finalZ[2]; - - // find all variables for 2 tracks at z0 = startPoint - // set A, B variables for x/y equation for 2 tracks - // calculate root - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateToZlinear(startPoint); - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - sinPhi0[i] = mCandTr[mCurHyp][i].getSnp(); - cosPhi0[i] = std::sqrt((1. - sinPhi0[i]) * (1. + sinPhi0[i])); - tanL0[i] = mCandTr[mCurHyp][i].getTanl(); - - Ax[i] = cosPhi0[i] / tanL0[i]; - Bx[i] = x[i] - X; - - Ay[i] = sinPhi0[i] / tanL0[i]; - By[i] = y[i] - Y; - - z12X[i] = -Bx[i] / Ax[i]; - z12Y[i] = -By[i] / Ay[i]; - - finalZ[i] = 0.5 * (z12X[i] + z12Y[i]); - } - - mPCA[mCurHyp][2] = 0.5 * (finalZ[0] + finalZ[1]); -} - -//___________________________________________________________________ -template -inline o2::track::TrackParFwd FwdDCAFitterN::FwdgetTrackParamAtPCA(int i, int icand) const -{ - // propagate tracks param only to current vertex (if not already done) - int ord = mOrder[icand]; - o2::track::TrackParFwd trc(mCandTr[ord][i]); - if (!mTrPropDone[ord]) { - auto z = mPCA[ord][2]; - trc.propagateParamToZquadratic(z, mBz); - } - - return {trc}; -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::getAbsMax(const VecND& v) -{ - double mx = -1; - for (int i = N; i--;) { - auto vai = std::abs(v[i]); - if (mx < vai) { - mx = vai; - } - } - return mx; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::minimizeChi2() -{ - // find best chi2 (weighted DCA) of N tracks in the vicinity of the seed PCA - double x[2], y[2]; - double sumX = 0.; - double sumY = 0.; - - for (int i = N; i--;) { - mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; - auto z = mPCA[mCurHyp][2]; - - mCandTr[mCurHyp][i].propagateToZquadratic(z, mBz); - - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], ZerrFactor); // prepare inverse cov.matrices at starting point - - sumX = sumX + x[i]; - sumY = sumY + y[i]; - } - - mPCA[mCurHyp][0] = sumX / N; - mPCA[mCurHyp][1] = sumY / N; - - if (mMaxDXIni > 0 && !roughDXCut()) { // apply rough cut on tracks X difference - return false; - } - - if (!FwdcalcPCACoefs()) { // prepare tracks contribution matrices to the global PCA - return false; - } - FwdcalcPCA(); // current PCA - FwdcalcTrackResiduals(); // current track residuals - float chi2Upd, chi2 = FwdcalcChi2(); - do { - calcTrackDerivatives(); // current track derivatives (1st and 2nd) - FwdcalcResidDerivatives(); // current residals derivatives (1st and 2nd) - FwdcalcChi2Derivatives(); // current chi2 derivatives (1st and 2nd) to proceed for dz calculation - - // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 - if (!mD2Chi2Dz2.Invert()) { - return false; - } - - VecND dz = mD2Chi2Dz2 * mDChi2Dz; - - if (!FwdcorrectTracks(dz)) { // calculate new Pi (mTrPos) following Newton-Rapson iteration - return false; - } - - FwdcalcPCA(); // updated mPCA (new V coordinates with new mTrPos (Pi)) - if (mCrossIDAlt >= 0 && closerToAlternative()) { - mAllowAltPreference = false; - return false; - } - - FwdcalcTrackResiduals(); // updated residuals - chi2Upd = FwdcalcChi2(); // updated chi2 - - if (getAbsMax(dz) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { - chi2 = chi2Upd; - break; // converged - } - - chi2 = chi2Upd; - } while (++mNIters[mCurHyp] < mMaxIter); - - mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::minimizeChi2NoErr() -{ - // find best chi2 (absolute DCA) of N tracks in the vicinity of the PCA seed - double x[2], y[2]; - double sumX = 0.; - double sumY = 0.; - - for (int i = N; i--;) { - - mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; - - auto z = mPCA[mCurHyp][2]; - mCandTr[mCurHyp][i].propagateParamToZquadratic(z, mBz); - - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - - mPCA[mCurHyp][2] = z; - - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - - sumX = sumX + x[i]; - sumY = sumY + y[i]; - } - - mPCA[mCurHyp][0] = sumX / N; - mPCA[mCurHyp][1] = sumY / N; - - if (mMaxDXIni > 0 && !roughDXCut()) { // apply rough cut on tracks Z difference - return false; - } - - FwdcalcPCANoErr(); // current PCA - FwdcalcTrackResiduals(); // current track residuals - float chi2Upd, chi2 = FwdcalcChi2NoErr(); - do { - calcTrackDerivatives(); // current track derivatives (1st and 2nd) - FwdcalcResidDerivativesNoErr(); // current residals derivatives (1st and 2nd) - FwdcalcChi2DerivativesNoErr(); // current chi2 derivatives (1st and 2nd) - - // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 - if (!mD2Chi2Dz2.Invert()) { - return false; - } - VecND dz = mD2Chi2Dz2 * mDChi2Dz; - - if (!FwdcorrectTracks(dz)) { - return false; - } - FwdcalcPCANoErr(); // updated PCA - if (mCrossIDAlt >= 0 && closerToAlternative()) { - mAllowAltPreference = false; - return false; - } - FwdcalcTrackResiduals(); // updated residuals - chi2Upd = FwdcalcChi2NoErr(); // updated chi2 - if (getAbsMax(dz) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { - chi2 = chi2Upd; - break; // converged - } - chi2 = chi2Upd; - } while (++mNIters[mCurHyp] < mMaxIter); - // - mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::roughDXCut() const -{ - // apply rough cut on DX 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].getX() - mCandTr[mCurHyp][j].getX()) > mMaxDXIni) { - accept = false; - break; - } - } - } - return accept; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::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]; - auto dxAlt = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDAlt], dyAlt = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDAlt]; - return dxCur * dxCur + dyCur * dyCur > dxAlt * dxAlt + dyAlt * dyAlt; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::print() const -{ - LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode"; - LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2; - LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change; - LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDXIni; -} -//___________________________________________________________________ -template -inline bool FwdDCAFitterN::propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const -{ - // propagate track to vertex including MCS effects if material budget included, simple propagation to Z otherwise - float x2x0 = 0; - if (mUseMatBudget) { - auto mb = mMatLUT->getMatBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); - x2x0 = (float)mb.meanX2X0; - return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); - } else if (mTGeoFallBackAllowed) { - auto geoMan = o2::base::GeometryManager::meanMaterialBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); - x2x0 = (float)geoMan.meanX2X0; - return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); - } else { - t.propagateToZhelix(p[2], mBz); - return true; - } -} - -using FwdDCAFitter2 = FwdDCAFitterN<2, o2::track::TrackParCovFwd>; -using FwdDCAFitter3 = FwdDCAFitterN<3, o2::track::TrackParCovFwd>; - -} // namespace vertexing -} // namespace o2 -#endif // _ALICEO2_DCA_FWDFITTERN_ diff --git a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h b/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h deleted file mode 100644 index 72066250f1053..0000000000000 --- a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file HelixHelper.h -/// \brief Helper classes for helical tracks manipulations -/// \author ruben.shahoyan@cern.ch - -#ifndef _ALICEO2_HELIX_HELPER_ -#define _ALICEO2_HELIX_HELPER_ - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include "MathUtils/Primitive2D.h" - -namespace o2 -{ -namespace track -{ - -///__________________________________________________________________________ -//< precalculated track radius, center, alpha sin,cos and their combinations -struct TrackAuxPar : public o2::math_utils::CircleXYf_t { - float c, s, cc, ss, cs; // cos ans sin of track alpha and their products - - TrackAuxPar() = default; - - template - TrackAuxPar(const T& trc, float bz) - { - set(trc, bz); - } - float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) - float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) - - template - void set(const T& trc, float bz) - { - trc.getCircleParams(bz, *this, s, c); - cc = c * c; - ss = s * s; - cs = c * s; - } - ClassDefNV(TrackAuxPar, 1); -}; - -//__________________________________________________________ -//< crossing coordinates of 2 circles -struct CrossInfo { - static constexpr float MaxDistXYDef = 10.; - float xDCA[2] = {}; - float yDCA[2] = {}; - int nDCA = 0; - - int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef) - { - const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A - const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; - float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; - float dist2 = xDist * xDist + yDist * yDist, dist = std::sqrt(dist2), rsum = trcA.rC + trcB.rC; - if (std::abs(dist) < 1e-12) { - return nDCA; // circles are concentric? - } - if (dist > rsum) { // circles don't touch, chose a point in between - // the parametric equation of lines connecting the centers is - // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) - if (dist - rsum > maxDistXY) { // too large distance - return nDCA; - } - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } else if (dist + trcB.rC < trcA.rC) { // the small circle is nestled into large one w/o touching - // select the point of closest approach of 2 circles - notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC); - } else { // 2 intersection points - // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that - // the 1st one is centered in origin - if (std::abs(xDist) < std::abs(yDist)) { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = std::sqrt(det); - xDCA[0] = (-ab + det) / (1. + b * b); - yDCA[0] = a + b * xDCA[0] + trcA.yC; - xDCA[0] += trcA.xC; - xDCA[1] = (-ab - det) / (1. + b * b); - yDCA[1] = a + b * xDCA[1] + trcA.yC; - xDCA[1] += trcA.xC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } else { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = std::sqrt(det); - yDCA[0] = (-ab + det) / (1. + bb); - xDCA[0] = a + b * yDCA[0] + trcA.xC; - yDCA[0] += trcA.yC; - yDCA[1] = (-ab - det) / (1. + bb); - xDCA[1] = a + b * yDCA[1] + trcA.xC; - yDCA[1] += trcA.yC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } - } - return nDCA; - } - - void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign) - { - // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: - // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist - // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... - // There are 2 special cases: - // (a) small circle is inside the large one: provide rBSign as -trcB.rC - // (b) circle are side by side: provide rBSign as trcB.rC - nDCA = 1; - auto t2d = (dist + trcA.rC - rBSign) / dist; - xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); - yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); - } - - template - int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of 2 straight lines - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 - - float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! - float dy = trax1.yC - trax0.yC; // - float dz = tr1.getZ() - tr0.getZ(); - auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 - auto csp0i = std::sqrt(csp0i2); - auto tgp0 = tr0.getSnp() * csp0i; - float kx0 = trax0.c - trax0.s * tgp0; - float ky0 = trax0.s + trax0.c * tgp0; - float kz0 = tr0.getTgl() * csp0i; - auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 - auto csp1i = std::sqrt(csp1i2); - auto tgp1 = tr1.getSnp() * std::sqrt(csp1i2); - float kx1 = trax1.c - trax1.s * tgp1; - float ky1 = trax1.s + trax1.c * tgp1; - float kz1 = tr1.getTgl() * csp1i; - /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach - /// Leads to system - /// A Dx = B with Dx = {dx0, dx1} - /// with A = - /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... - /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 - /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } - /// - float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); - float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); - float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; - if (std::abs(det) > o2::constants::math::Almost0) { - auto detI = 1. / det; - auto t0 = det0 * detI; - auto t1 = det1 * detI; - float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; - dx += addx1 - addx0; // recalculate XY distance at DCA - dy += addy1 - addy0; - if (dx * dx + dy * dy > maxDistXY * maxDistXY) { - return nDCA; - } - xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; - yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of line and circle - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 = 1 / csp^2 - - const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) - const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line - const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line - - // solve quadratic equation of line crossing the circle - float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center - float dy = traxL.yC - traxH.yC; // Y... - // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 - auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 - auto cspi = std::sqrt(cspi2); - auto tgp = trcL.getSnp() * cspi; - float kx = traxL.c - traxL.s * tgp; - float ky = traxL.s + traxL.c * tgp; - double dk = dx * kx + dy * ky; - double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); - if (det > 0) { // 2 crossings - det = std::sqrt(det); - float t0 = (-dk + det) * cspi2; - float t1 = (-dk - det) * cspi2; - xDCA[0] = traxL.xC + kx * t0; - yDCA[0] = traxL.yC + ky * t0; - xDCA[1] = traxL.xC + kx * t1; - yDCA[1] = traxL.yC + ky * t1; - nDCA = 2; - } else { - // there is no crossing, find the point of the closest approach on the line which is closest to the circle center - float t = -dk * cspi2; - float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle - float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = std::sqrt(dxc * dxc + dyc * dyc); - if (dist - traxH.rC > maxDistXY) { // too large distance - return nDCA; - } - float drcf = traxH.rC / dist; // radius / distance to circle center - float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; - xDCA[0] = (xL + xH) * 0.5; - yDCA[0] = (yL + yH) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - // calculate up to 2 crossings between 2 circles - nDCA = 0; - if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines - nDCA = circlesCrossInfo(trax0, trax1, maxDistXY); - } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines - nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } else { - nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } - // - return nDCA; - } - - CrossInfo() = default; - - template - CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - set(trax0, tr0, trax1, tr1, maxDistXY); - } - ClassDefNV(CrossInfo, 1); -}; - -} // namespace track -} // namespace o2 - -#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h index 9967cbfcd5642..c06c2119b0cd1 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h @@ -113,7 +113,7 @@ class PVertexer bool prepareVertexRefit(const TR& tracks, const o2d::VertexBase& vtxSeed); PVertex refitVertex(const std::vector useTrack, const o2d::VertexBase& vtxSeed); - + PVertex refitVertexFull(const std::vector useTrack, const o2d::VertexBase& vtxSeed); auto getNTZClusters() const { return mNTZClustersIni; } auto getTotTrials() const { return mTotTrials; } auto getMaxTrialsPerCluster() const { return mMaxTrialPerCluster; } @@ -135,6 +135,7 @@ class PVertexer void setPoolDumpDirectory(const std::string& d) { mPoolDumpDirectory = d; } void printInpuTracksStatus(const VertexingInput& input) const; + void initMeanVertexConstraint(); private: static constexpr int DBS_UNDEF = -2, DBS_NOISE = -1, DBS_INCHECK = -10; @@ -152,7 +153,6 @@ class PVertexer FitStatus evalIterations(VertexSeed& vtxSeed, PVertex& vtx) const; TimeEst timeEstimate(const VertexingInput& input) const; float findZSeedHistoPeak() const; - void initMeanVertexConstraint(); void applyConstraint(VertexSeed& vtxSeed) const; bool upscaleSigma(VertexSeed& vtxSeed) const; bool relateTrackToMeanVertex(o2::track::TrackParCov& trc, float vtxErr2); diff --git a/Detectors/Vertexing/src/PVertexer.cxx b/Detectors/Vertexing/src/PVertexer.cxx index 5fea1943ac762..10e504bba0772 100644 --- a/Detectors/Vertexing/src/PVertexer.cxx +++ b/Detectors/Vertexing/src/PVertexer.cxx @@ -1333,6 +1333,37 @@ PVertex PVertexer::refitVertex(const std::vector useTrack, const o2d::Vert return vtxRes; } +//______________________________________________ +PVertex PVertexer::refitVertexFull(const std::vector useTrack, const o2d::VertexBase& vtxSeed) +{ + // Use this method if because of e.g. different alingnment the new vertex is supposed to be shifted from the original one. + // Refit the tracks prepared by the successful prepareVertexRefit, possible skipping those tracks wich have useTrack value false + // (useTrack is ignored if empty). + // The vtxSeed is the originally found vertex, assumed to be the same original PV used for the prepareVertexRefit. + // Refitted PrimaryVertex is returned, negative chi2 means failure of the refit. + // ATTENTION: only the position is refitted, the vertex time and IRMin/IRMax info is dummy. + + if (vtxSeed != mVtxRefitOrig) { + throw std::runtime_error("refitVertex must be preceded by successful prepareVertexRefit"); + } + VertexingInput inp; + inp.scaleSigma2 = mPVParams->iniScale2; + inp.idRange = gsl::span(mRefitTrackIDs); + if (useTrack.size()) { + for (uint32_t i = 0; i < mTracksPool.size(); i++) { + mTracksPool[i].vtxID = useTrack[mTracksPool[i].entry] ? TrackVF::kNoVtx : TrackVF::kDiscarded; + } + } + PVertex vtxRes{}; + vtxRes.VertexBase::operator=(vtxSeed); + if (findVertex(inp, vtxRes)) { + vtxRes.setTimeStamp({0.f, -1.}); // time is not refitter + } else { + vtxRes.setChi2(-1.); + } + return vtxRes; +} + //______________________________________________ void PVertexer::printInpuTracksStatus(const VertexingInput& input) const { diff --git a/Detectors/Vertexing/src/SVertexer.cxx b/Detectors/Vertexing/src/SVertexer.cxx index 1d48bcceb0097..d9206fe54e068 100644 --- a/Detectors/Vertexing/src/SVertexer.cxx +++ b/Detectors/Vertexing/src/SVertexer.cxx @@ -269,6 +269,9 @@ void SVertexer::updateTimeDependentParams() if (!updatedOnce) { updatedOnce = true; mSVParams = &SVertexerParams::Instance(); + if (mSVParams->mExcludeTPCtracks && !mRecoCont->isTrackSourceLoaded(GIndex::TPC)) { + LOGP(fatal, "TPC tracks requested but not provided"); + } // precalculated selection cuts mMinR2ToMeanVertex = mSVParams->minRToMeanVertex * mSVParams->minRToMeanVertex; mMaxR2ToMeanVertexCascV0 = mSVParams->maxRToMeanVertexCascV0 * mSVParams->maxRToMeanVertexCascV0; @@ -543,8 +546,7 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a } continue; } - - if (!hasTPC && nITSclu < mSVParams->mITSSAminNclu && (!shortOBITSOnlyTrack || mSVParams->mRejectITSonlyOBtrack)) { + if ((isTPCloaded && !hasTPC) && (isITSloaded && (nITSclu < mSVParams->mITSSAminNclu && (!shortOBITSOnlyTrack || mSVParams->mRejectITSonlyOBtrack)))) { continue; // reject short ITS-only } diff --git a/Detectors/ZDC/macro/CreateBaselineCalib.C b/Detectors/ZDC/macro/CreateBaselineCalib.C index e6f2be3cb0f93..d04543fb52091 100644 --- a/Detectors/ZDC/macro/CreateBaselineCalib.C +++ b/Detectors/ZDC/macro/CreateBaselineCalib.C @@ -79,7 +79,7 @@ void CreateBaselineCalib(long tmin = 0, long tmax = -1, std::string ccdbHost = " } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateBaselineCalibConfig.C b/Detectors/ZDC/macro/CreateBaselineCalibConfig.C index be31f65941baa..c2fde8f1ab1a8 100644 --- a/Detectors/ZDC/macro/CreateBaselineCalibConfig.C +++ b/Detectors/ZDC/macro/CreateBaselineCalibConfig.C @@ -48,7 +48,7 @@ void CreateBaselineCalibConfig(long tmin = 0, long tmax = -1, std::string ccdbHo } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateEnergyCalib.C b/Detectors/ZDC/macro/CreateEnergyCalib.C index 23befda355768..3ff5ff5537cf7 100644 --- a/Detectors/ZDC/macro/CreateEnergyCalib.C +++ b/Detectors/ZDC/macro/CreateEnergyCalib.C @@ -58,7 +58,7 @@ void CreateEnergyCalib(long tmin = 0, long tmax = -1, std::string ccdbHost = "") } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateInterCalibConfig.C b/Detectors/ZDC/macro/CreateInterCalibConfig.C index 915b55b42d2eb..ce81b7f9ee3b5 100644 --- a/Detectors/ZDC/macro/CreateInterCalibConfig.C +++ b/Detectors/ZDC/macro/CreateInterCalibConfig.C @@ -61,7 +61,7 @@ void CreateInterCalibConfig(long tmin = 0, long tmax = -1, std::string ccdbHost } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateModuleConfig.C b/Detectors/ZDC/macro/CreateModuleConfig.C index 2d5fde58e3c41..d9d76dd85deb1 100644 --- a/Detectors/ZDC/macro/CreateModuleConfig.C +++ b/Detectors/ZDC/macro/CreateModuleConfig.C @@ -150,7 +150,7 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, std::string ccdbHost = "" } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateRecoConfigZDC.C b/Detectors/ZDC/macro/CreateRecoConfigZDC.C index c504241346787..838cd93b944c4 100644 --- a/Detectors/ZDC/macro/CreateRecoConfigZDC.C +++ b/Detectors/ZDC/macro/CreateRecoConfigZDC.C @@ -150,7 +150,7 @@ void CreateRecoConfigZDC(long tmin = 0, long tmax = -1, std::string ccdbHost = " } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateSimCondition.C b/Detectors/ZDC/macro/CreateSimCondition.C index 6349adbf5b66e..9f29aafc979bb 100644 --- a/Detectors/ZDC/macro/CreateSimCondition.C +++ b/Detectors/ZDC/macro/CreateSimCondition.C @@ -148,7 +148,7 @@ void CreateSimCondition(long tmin = 0, long tmax = -1, std::string ccdbHost = "" } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateSimCondition_pp.C b/Detectors/ZDC/macro/CreateSimCondition_pp.C index 6d3c530b6772b..f38634ef18fac 100644 --- a/Detectors/ZDC/macro/CreateSimCondition_pp.C +++ b/Detectors/ZDC/macro/CreateSimCondition_pp.C @@ -101,7 +101,7 @@ void CreateSimCondition_pp(long tmin = 0, long tmax = -1, std::string ccdbHost = conf.print(); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty if (ccdbHost.size() == 0 || ccdbHost == "external") { ccdbHost = "http://alice-ccdb.cern.ch:8080"; } else if (ccdbHost == "internal") { diff --git a/Detectors/ZDC/macro/CreateTDCCalib.C b/Detectors/ZDC/macro/CreateTDCCalib.C index 1591e1e31f699..44e94f84334b1 100644 --- a/Detectors/ZDC/macro/CreateTDCCalib.C +++ b/Detectors/ZDC/macro/CreateTDCCalib.C @@ -63,7 +63,7 @@ void CreateTDCCalib(long tmin = 0, long tmax = -1, std::string ccdbHost = "", fl } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "Storing " << o2::zdc::CCDBPathTDCCalib << " on CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateTDCCalibConfig.C b/Detectors/ZDC/macro/CreateTDCCalibConfig.C index 4aafbf555f088..2511e4f832add 100644 --- a/Detectors/ZDC/macro/CreateTDCCalibConfig.C +++ b/Detectors/ZDC/macro/CreateTDCCalibConfig.C @@ -60,7 +60,7 @@ void CreateTDCCalibConfig(long tmin = 0, long tmax = -1, std::string ccdbHost = } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateTDCCorr.C b/Detectors/ZDC/macro/CreateTDCCorr.C index 9c089f532a408..45845dd669a81 100644 --- a/Detectors/ZDC/macro/CreateTDCCorr.C +++ b/Detectors/ZDC/macro/CreateTDCCorr.C @@ -99,7 +99,7 @@ void CreateTDCCorr(long tmin = 0, long tmax = -1, std::string ccdbHost = "") } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/macro/CreateTowerCalib.C b/Detectors/ZDC/macro/CreateTowerCalib.C index 16e67e448cd65..18e7dc145c154 100644 --- a/Detectors/ZDC/macro/CreateTowerCalib.C +++ b/Detectors/ZDC/macro/CreateTowerCalib.C @@ -75,7 +75,7 @@ void CreateTowerCalib(long tmin = 0, long tmax = -1, std::string ccdbHost = "") } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdb_host.c_str()); LOG(info) << "CCDB server: " << api.getURL(); // store abitrary user object in strongly typed manner diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h index c2f2163600f29..a299431ef17fc 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h @@ -32,10 +32,10 @@ namespace o2 namespace zdc { -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::ZDC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::ZDC, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/ZDC/simulation/src/Detector.cxx b/Detectors/ZDC/simulation/src/Detector.cxx index c6f91d9c2164f..b8b81379a4dff 100644 --- a/Detectors/ZDC/simulation/src/Detector.cxx +++ b/Detectors/ZDC/simulation/src/Detector.cxx @@ -2355,7 +2355,7 @@ void Detector::createDetectors() // --- Positioning the ZEM into the ZDC - rotation for 90 degrees // NB -> ZEM is positioned in cave volume - const float z0 = 1313.3475; // center of caveRB24 mother volume + const float z0 = 1313.3475 + 75.; // center of caveRB24 mother volume TVirtualMC::GetMC()->Gspos("ZEM ", 1, "caveRB24", -Geometry::ZEMPOSITION[0], Geometry::ZEMPOSITION[1], Geometry::ZEMPOSITION[2] + Geometry::ZEMDIMENSION[0] - z0, irotzem1, "ONLY"); // Second EM ZDC (same side w.r.t. IP, just on the other side w.r.t. beam pipe) diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h index ae53ca8bdd0fb..6226b4dc99fe3 100644 --- a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace zdc 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 zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h index 4979de5a30332..44c4585bf0c3f 100644 --- a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace zdc 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; @@ -43,7 +43,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 zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx index bf870324ce442..59c774662525a 100644 --- a/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace zdc { - -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(); @@ -81,7 +80,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{{"trig"}, "ZDC", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -91,17 +90,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_ZDC", "ZDC", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_ZDC", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_ZDC", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "zdc-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 zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx index abbd821fcb749..1a12360645ab2 100644 --- a/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx @@ -27,8 +27,7 @@ namespace o2 { namespace zdc { - -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(); @@ -74,13 +73,16 @@ 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("trig", "ZDC", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("chan", "ZDC", "DIGITSCH", 0, Lifetime::Timeframe); inputs.emplace_back("peds", "ZDC", "DIGITSPD", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -89,13 +91,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"ZDC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "ZDC", "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 zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx index 070c65ac9196a..9ab0e10098f43 100644 --- a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ZDC/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, "ccdb", {"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); @@ -38,6 +39,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); - wf.emplace_back(o2::zdc::getEntropyEncoderSpec(selIR)); + wf.emplace_back(o2::zdc::getEntropyEncoderSpec(selIR, cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/gconfig/g4Config.C b/Detectors/gconfig/g4Config.C index 8f74c0105dbf5..c2b1fbd433e4b 100644 --- a/Detectors/gconfig/g4Config.C +++ b/Detectors/gconfig/g4Config.C @@ -100,7 +100,16 @@ void Config() auto& g4Params = ::o2::conf::G4Params::Instance(); auto& physicsSetup = g4Params.getPhysicsConfigString(); std::cout << "PhysicsSetup wanted " << physicsSetup << "\n"; - auto runConfiguration = new TG4RunConfiguration("geomRoot", physicsSetup, "stepLimiter+specialCuts", + std::string geomNavStr; + if (g4Params.navmode == o2::conf::EG4Nav::kTGeo) { + geomNavStr = "geomRoot"; + } else if (g4Params.navmode == o2::conf::EG4Nav::kG4) { + geomNavStr = "geomVMC+RootToGeant4"; + } else { + LOG(fatal) << "Unsupported geometry navigation mode"; + } + + auto runConfiguration = new TG4RunConfiguration(geomNavStr, physicsSetup, "stepLimiter+specialCuts", specialStacking, mtMode); /// avoid the use of G4BACKTRACE (it seems to inferfere with process logic in o2-sim) setenv("G4BACKTRACE", "none", 1); diff --git a/Detectors/gconfig/src/StandardSteppingTrackRefHook.macro b/Detectors/gconfig/src/StandardSteppingTrackRefHook.macro index e408bbff420b5..3888d3c74ca8d 100644 --- a/Detectors/gconfig/src/StandardSteppingTrackRefHook.macro +++ b/Detectors/gconfig/src/StandardSteppingTrackRefHook.macro @@ -8,7 +8,7 @@ o2::steer::O2MCApplicationBase::TrackRefFcn trackRefHook() { if (vmc->IsTrackStop() && stack->currentTrackLeftTrackRef()) { // we add a stopping TrackRef when the current track already // registered previous TrackRefs - stack->addTrackReference(o2::TrackReference(*vmc, 0)); + stack->addTrackReference(o2::TrackReference(*vmc, -1)); } }; } \ No newline at end of file diff --git a/EventVisualisation/Base/src/DirectoryLoader.cxx b/EventVisualisation/Base/src/DirectoryLoader.cxx index 1b0135428806f..e106eaf7ebb47 100644 --- a/EventVisualisation/Base/src/DirectoryLoader.cxx +++ b/EventVisualisation/Base/src/DirectoryLoader.cxx @@ -14,6 +14,8 @@ /// \author julian.myrcha@cern.ch #include "EventVisualisationBase/DirectoryLoader.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/DataTakingContext.h" #include #include #include @@ -29,10 +31,14 @@ using namespace o2::event_visualisation; deque DirectoryLoader::load(const std::string& path, const std::string& marker, const std::vector& ext) { deque result; - for (const auto& entry : std::filesystem::directory_iterator(path)) { - if (std::find(ext.begin(), ext.end(), entry.path().extension()) != ext.end()) { - result.push_back(entry.path().filename()); + try { + for (const auto& entry : std::filesystem::directory_iterator(path)) { + if (std::find(ext.begin(), ext.end(), entry.path().extension()) != ext.end()) { + result.push_back(entry.path().filename()); + } } + } catch (std::filesystem::filesystem_error const& ex) { + LOGF(error, "filesystem problem during DirectoryLoader::load: %s", ex.what()); } // comparison with safety if marker not in the filename (-1+1 gives 0) std::sort(result.begin(), result.end(), @@ -56,15 +62,18 @@ bool DirectoryLoader::canCreateNextFile(const std::vector& paths, c } } } catch (std::filesystem::filesystem_error const& ex) { - LOGF(info, "filesystem problem: %s", ex.what()); + LOGF(error, "filesystem problem during DirectoryLoader::canCreateNextFile: %s", ex.what()); } } // comparison with safety if marker not in the filename (-1+1 gives 0) - std::ranges::sort(result.begin(), result.end(), - [marker](const std::string& a, const std::string& b) { - return a.substr(a.find_first_of(marker) + 1) > b.substr(b.find_first_of(marker) + 1); - }); + if (result.size() > 1) { + std::ranges::sort(result.begin(), result.end(), + [marker](const std::string& a, const std::string& b) { + return a.substr(a.find_first_of(marker) + 1) > b.substr(b.find_first_of(marker) + 1); + }); + } + unsigned long accumulatedSize = 0L; const std::regex delimiter{"_"}; for (auto const& file : result) { @@ -87,12 +96,16 @@ bool DirectoryLoader::canCreateNextFile(const std::vector& paths, c deque DirectoryLoader::load(const std::vector& paths, const std::string& marker, const std::vector& ext) { deque result; - for (const auto& path : paths) { - for (const auto& entry : std::filesystem::directory_iterator(path)) { - if (std::find(ext.begin(), ext.end(), entry.path().extension()) != ext.end()) { - result.push_back(entry.path().filename()); + try { + for (const auto& path : paths) { + for (const auto& entry : std::filesystem::directory_iterator(path)) { + if (std::find(ext.begin(), ext.end(), entry.path().extension()) != ext.end()) { + result.push_back(entry.path().filename()); + } } } + } catch (std::filesystem::filesystem_error const& ex) { + LOGF(error, "filesystem problem during DirectoryLoader::load: %s", ex.what()); } // comparison with safety if marker not in the filename (-1+1 gives 0) std::sort(result.begin(), result.end(), @@ -105,11 +118,15 @@ deque DirectoryLoader::load(const std::vector& paths, const std::vector DirectoryLoader::allFolders(const std::string& location) { - auto const pos = location.find_last_of('_'); std::vector folders; - folders.push_back(location.substr(0, pos) + "_PHYSICS"); - folders.push_back(location.substr(0, pos) + "_COSMICS"); - folders.push_back(location.substr(0, pos) + "_SYNTHETIC"); + if (o2::framework::DefaultsHelpers::deploymentMode() == o2::framework::DeploymentMode::OnlineDDS) { + auto const pos = location.find_last_of('_'); + folders.push_back(location.substr(0, pos) + "_PHYSICS"); + folders.push_back(location.substr(0, pos) + "_COSMICS"); + folders.push_back(location.substr(0, pos) + "_SYNTHETIC"); + } else { + folders.push_back(location); + } return folders; } @@ -135,10 +152,14 @@ std::time_t to_time_t(TP tp) int DirectoryLoader::getNumberOfFiles(const std::string& path, std::vector& ext) { int res = 0; - for (const auto& entry : std::filesystem::directory_iterator(path)) { - if (std::find(ext.begin(), ext.end(), entry.path().extension()) != ext.end()) { - res++; + try { + for (const auto& entry : std::filesystem::directory_iterator(path)) { + if (std::find(ext.begin(), ext.end(), entry.path().extension()) != ext.end()) { + res++; + } } + } catch (std::filesystem::filesystem_error const& ex) { + LOGF(error, "filesystem problem during DirectoryLoader::getNumberOfFiles: %s", ex.what()); } return res; } @@ -160,8 +181,12 @@ std::string DirectoryLoader::getLatestFile(const std::string& path, std::vector< void DirectoryLoader::removeOldestFiles(const std::string& path, std::vector& ext, const int remaining) { - while (getNumberOfFiles(path, ext) > remaining) { - LOGF(info, "removing oldest file in folder: %s : %s", path, getLatestFile(path, ext)); - filesystem::remove(path + "/" + getLatestFile(path, ext)); + try { + while (getNumberOfFiles(path, ext) > remaining) { + LOGF(info, "removing oldest file in folder: %s : %s", path, getLatestFile(path, ext)); + filesystem::remove(path + "/" + getLatestFile(path, ext)); + } + } catch (std::filesystem::filesystem_error const& ex) { + LOGF(error, "filesystem problem during DirectoryLoader::removeOldestFiles: %s", ex.what()); } } diff --git a/EventVisualisation/DataConverter/CMakeLists.txt b/EventVisualisation/DataConverter/CMakeLists.txt index 778a3b6182aaf..b0198000c3dbe 100644 --- a/EventVisualisation/DataConverter/CMakeLists.txt +++ b/EventVisualisation/DataConverter/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(EventVisualisationDataConverter src/VisualisationEventJSONSerializer.cxx src/VisualisationEventROOTSerializer.cxx src/VisualisationEventOpenGLSerializer.cxx + src/Location.cxx PUBLIC_LINK_LIBRARIES RapidJSON::RapidJSON O2::ReconstructionDataFormats O2::DataFormatsParameters @@ -33,6 +34,7 @@ o2_add_executable(eve-convert src/VisualisationTrack.cxx src/VisualisationCluster.cxx src/VisualisationCalo.cxx + src/Location.cxx PUBLIC_LINK_LIBRARIES O2::EventVisualisationView RapidJSON::RapidJSON diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/Location.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/Location.h new file mode 100644 index 0000000000000..72ebc36b1dd31 --- /dev/null +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/Location.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 Location.h +/// \author Julian Myrcha +/// + +#ifndef O2EVE_LOCATION_H +#define O2EVE_LOCATION_H + +#include +#include +#include + +namespace o2::event_visualisation +{ +struct LocationParams { + std::string fileName; + int port = -1; + int timeout = 100; + std::string host = "localhost"; + bool toFile = true; + bool toSocket = true; +}; +class Location +{ + std::ofstream* mOut; + int mClientSocket; + bool mToFile; + bool mToSocket; + std::string mFileName; + int mPort; + int mTimeout; + std::string mHostName; + + public: + explicit Location(const LocationParams& params) + { + this->mFileName = params.fileName; + this->mToFile = !params.fileName.empty() && params.toFile; + this->mToSocket = params.port != -1 && params.toSocket; + this->mOut = nullptr; + this->mPort = params.port; + this->mHostName = params.host; + this->mClientSocket = -1; + this->mTimeout = params.timeout; + } + ~Location() + { + close(); + } + void open(); + void close(); + void write(char* buf, std::streamsize size); + [[nodiscard]] std::string fileName() const { return this->mFileName; } + [[nodiscard]] std::string hostName() const { return this->mHostName; } + [[nodiscard]] int port() const { return this->mPort; } +}; +} // namespace o2::event_visualisation + +#endif // O2EVE_LOCATION_H diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h index c08009215d9fe..8f4b0d2bd8375 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h @@ -56,7 +56,7 @@ class VisualisationEventJSONSerializer : public VisualisationEventSerializer public: bool fromFile(VisualisationEvent& event, std::string fileName) override; - void toFile(const VisualisationEvent& event, std::string fileName) override; + void toFile(const VisualisationEvent& event, Location& location) override; ~VisualisationEventJSONSerializer() override = default; }; diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventOpenGLSerializer.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventOpenGLSerializer.h index 3e6d3809cb709..8af91d6964a46 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventOpenGLSerializer.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventOpenGLSerializer.h @@ -38,7 +38,7 @@ class VisualisationEventOpenGLSerializer : public VisualisationEventSerializer public: const std::string serializerName() const override { return std::string("VisualisationEventOpenGLSerializer"); } bool fromFile(VisualisationEvent& event, std::string fileName) override; - void toFile(const VisualisationEvent& event, std::string fileName) override; + void toFile(const VisualisationEvent& event, Location& location) override; ~VisualisationEventOpenGLSerializer() override = default; }; diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventROOTSerializer.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventROOTSerializer.h index e6408fb1c6c3f..3a5cf245f4816 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventROOTSerializer.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventROOTSerializer.h @@ -44,7 +44,7 @@ class VisualisationEventROOTSerializer : public VisualisationEventSerializer public: [[nodiscard]] const std::string serializerName() const override { return std::string("VisualisationEventROOTSerializer"); } bool fromFile(VisualisationEvent& event, std::string fileName) override; - void toFile(const VisualisationEvent& event, std::string fileName) override; + void toFile(const VisualisationEvent& event, Location& location) override; ~VisualisationEventROOTSerializer() override = default; }; diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventSerializer.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventSerializer.h index 5a6d902084ebf..80cb3c0b131a0 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventSerializer.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventSerializer.h @@ -17,6 +17,7 @@ #define O2EVE_VISUALISATIONEVENTSERIALIZER_H #include "EventVisualisationDataConverter/VisualisationEvent.h" +#include "EventVisualisationDataConverter/Location.h" #include #include @@ -45,7 +46,7 @@ class VisualisationEventSerializer static o2::dataformats::GlobalTrackID deserialize(unsigned source, unsigned index, unsigned flags); static VisualisationEventSerializer* getInstance(std::string ext) { return instances[ext]; } virtual bool fromFile(VisualisationEvent& event, std::string fileName) = 0; - virtual void toFile(const VisualisationEvent& event, std::string fileName) = 0; + virtual void toFile(const VisualisationEvent& event, Location& location) = 0; virtual const std::string serializerName() const = 0; virtual ~VisualisationEventSerializer() = default; }; diff --git a/EventVisualisation/DataConverter/src/Location.cxx b/EventVisualisation/DataConverter/src/Location.cxx new file mode 100644 index 0000000000000..416412c742252 --- /dev/null +++ b/EventVisualisation/DataConverter/src/Location.cxx @@ -0,0 +1,182 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file Location.cxx +/// \author Julian Myrcha +/// + +#include "EventVisualisationDataConverter/Location.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace o2::event_visualisation +{ + +int connect_with_timeout(const int socket, const struct sockaddr* addr, socklen_t addrlen, const unsigned int timeout_ms) +{ + int connection = 0; + // Setting O_NONBLOCK + int socket_flags_before; + if ((socket_flags_before = fcntl(socket, F_GETFL, 0) < 0)) { + return -1; + } + if (fcntl(socket, F_SETFL, socket_flags_before | O_NONBLOCK) < 0) { + return -1; + } + do { + if (connect(socket, addr, addrlen) < 0) { + if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { + connection = -1; // error + } else { // wait for complete + // deadline 'timeout' ms from now + timespec now; // NOLINT(*-pro-type-member-init) + if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) { + connection = -1; + break; + } + const timespec deadline = {.tv_sec = now.tv_sec, + .tv_nsec = now.tv_nsec + timeout_ms * 1000000l}; + do { + if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) { + connection = -1; + break; + } + // compute remaining deadline + const int ms_until_deadline = static_cast((deadline.tv_sec - now.tv_sec) * 1000l + (deadline.tv_nsec - now.tv_nsec) / 1000000l); + if (ms_until_deadline < 0) { + connection = 0; + break; + } + pollfd connectionPool[] = {{.fd = socket, .events = POLLOUT}}; + connection = poll(connectionPool, 1, ms_until_deadline); + + if (connection > 0) { // confirm the success + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { + errno = error; + } + if (error != 0) { + connection = -1; + } + } + } while (connection == -1 && errno == EINTR); // If interrupted, try again. + if (connection == 0) { + errno = ETIMEDOUT; + connection = -1; + } + } + } + } while (false); + // Restore socket state + if (fcntl(socket, F_SETFL, socket_flags_before) < 0) { + return -1; + } + return connection; +} + +void Location::open() +{ + if (this->mToFile) { + this->mOut = new std::ofstream(mFileName, std::ios::out | std::ios::binary); + } + if (this->mToSocket) { + // resolve host name + sockaddr_in serverAddress; // NOLINT(*-pro-type-member-init) + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(this->mPort); // Port number + + // ask once + static auto server = gethostbyname(this->mHostName.c_str()); + if (server == nullptr) { + LOGF(info, "Error no such host %s", this->mHostName.c_str()); + return; + }; + + bcopy((char*)server->h_addr, + (char*)&serverAddress.sin_addr.s_addr, + server->h_length); + + // Connect to the server + this->mClientSocket = socket(AF_INET, SOCK_STREAM, 0); + if (this->mClientSocket == -1) { + LOGF(info, "Error creating socket"); + return; + } + + if (connect_with_timeout(this->mClientSocket, (sockaddr*)&serverAddress, + sizeof(serverAddress), this->mTimeout) == -1) { + LOGF(info, "Error connecting to %s:%d", this->mHostName.c_str(), this->mPort); + ::close(this->mClientSocket); + this->mClientSocket = -1; + return; + } + try { + char buf[256] = "SEND:"; + strncpy(buf + 6, this->mFileName.c_str(), sizeof(buf) - 7); + strncpy(buf + sizeof(buf) - 6, "ALICE", 6); + auto real = send(this->mClientSocket, buf, sizeof(buf), 0); + if (real != sizeof(buf)) { + throw real; + } + } catch (...) { + ::close(this->mClientSocket); + this->mClientSocket = -1; + LOGF(info, "Error sending file name to %s:%d", this->mHostName.c_str(), this->mPort); + } + } +} + +void Location::close() +{ + if (this->mToFile && this->mOut) { + this->mOut->close(); + delete this->mOut; + this->mOut = nullptr; + } + if (this->mToSocket && this->mClientSocket != -1) { + ::close(this->mClientSocket); + this->mClientSocket = -1; + } +} + +void Location::write(char* buf, std::streamsize size) +{ + if (size == 0) { + return; + } + if (this->mToFile && this->mOut) { + this->mOut->write(buf, size); + } + if (this->mToSocket && this->mClientSocket != -1) { + LOGF(info, "Location::write() socket %s ++++++++++++++++++++++", fileName()); + try { + auto real = send(this->mClientSocket, buf, size, 0); + if (real != size) { + throw real; + } + } catch (...) { + ::close(this->mClientSocket); + this->mClientSocket = -1; + LOGF(info, "Error sending data to %s:%d", this->mHostName.c_str(), this->mPort); + } + } +} + +} // namespace o2::event_visualisation \ No newline at end of file diff --git a/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx b/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx index 612ddaf8717f4..cce3e6b4a2c58 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx @@ -29,8 +29,9 @@ using namespace rapidjson; namespace o2::event_visualisation { -void VisualisationEventJSONSerializer::toFile(const VisualisationEvent& event, std::string fileName) +void VisualisationEventJSONSerializer::toFile(const VisualisationEvent& event, Location& location) { + std::string fileName = location.fileName(); std::string json = toJson(event); std::ofstream out(fileName); out << json; diff --git a/EventVisualisation/DataConverter/src/VisualisationEventOpenGLSerializer.cxx b/EventVisualisation/DataConverter/src/VisualisationEventOpenGLSerializer.cxx index 4c907eeda0291..1d22a02df61af 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEventOpenGLSerializer.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEventOpenGLSerializer.cxx @@ -20,6 +20,7 @@ #include #include #include +#include "EventVisualisationDataConverter/Location.h" namespace o2::event_visualisation { @@ -82,8 +83,9 @@ const auto CALT = "CALT"; // calo PID const auto FINE = "FINE"; // -void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, std::string fileName) +void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, Location& location) { + std::string fileName = location.fileName(); static const std::vector det_coma = { "ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "ITS-TPC", "TPC-TOF", "TPC-TRD", "MFT-MCH", "ITS-TPC-TRD", "ITS-TPC-TOF", "TPC-TRD-TOF", "MFT-MCH-MID", "ITS-TPC-TRD-TOF", "ITS-AB", "CTP", @@ -91,7 +93,9 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, std::ostringstream buf; constexpr auto SIGSIZE = 512; unsigned char data[SIGSIZE]; - std::ofstream out(fileName, std::ios::out | std::ios::binary); + // std::ofstream out(fileName, std::ios::out | std::ios::binary); + + location.open(); // head --bytes 512 fileName.eve buf << "eve" << std::endl; buf << "version=1.00" << std::endl; @@ -104,7 +108,7 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, memcpy((char*)&data[0], buf.str().c_str(), SIGSIZE); data[SIGSIZE - 2] = '\n'; data[SIGSIZE - 1] = 0; - out.write((char*)&data[0], SIGSIZE); // <----0 SIGN + location.write((char*)&data[0], SIGSIZE); // <----0 SIGN const auto trackNo = event.getTracksSpan().size(); int phsCount = 0; @@ -140,7 +144,7 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, head[Header::emcCount] = emcCount; head[Header::primaryVertex] = event.getPrimaryVertex(); head[Header::tfCounter] = event.getTfCounter(); - out.write(static_cast(chunkHEAD), chunkSize(chunkHEAD)); // <----1 HEAD + location.write(static_cast(chunkHEAD), chunkSize(chunkHEAD)); // <----1 HEAD free(chunkHEAD); } @@ -171,15 +175,15 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, celm[index] = track.getClusterCount(); index++; } - out.write(static_cast(chunkTTYP), chunkSize(chunkTTYP)); // <----2 TTYP + location.write(static_cast(chunkTTYP), chunkSize(chunkTTYP)); // <----2 TTYP free(chunkTTYP); - out.write(static_cast(chunkTELM), chunkSize(chunkTELM)); // <----3 TELM + location.write(static_cast(chunkTELM), chunkSize(chunkTELM)); // <----3 TELM free(chunkTELM); - out.write(static_cast(chunkCELM), chunkSize(chunkCELM)); // <----3 CELM + location.write(static_cast(chunkCELM), chunkSize(chunkCELM)); // <----3 CELM free(chunkCELM); - out.write(static_cast(chunkTGID), chunkSize(chunkTGID)); // <----3 GIND + location.write(static_cast(chunkTGID), chunkSize(chunkTGID)); // <----3 GIND free(chunkTGID); - out.write(static_cast(chunkTPID), chunkSize(chunkTPID)); // <----3 TPID (tracks pid) + location.write(static_cast(chunkTPID), chunkSize(chunkTPID)); // <----3 TPID (tracks pid) free(chunkTPID); } @@ -230,17 +234,17 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, cxyz[cidx++] = track.getClustersSpan()[i].Z(); } } - out.write(static_cast(chunkTXYZ), chunkSize(chunkTXYZ)); // <----4 TXYZ + location.write(static_cast(chunkTXYZ), chunkSize(chunkTXYZ)); // <----4 TXYZ free(chunkTXYZ); - out.write(static_cast(chunkCXYZ), chunkSize(chunkCXYZ)); // <----4 CXYZ + location.write(static_cast(chunkCXYZ), chunkSize(chunkCXYZ)); // <----4 CXYZ free(chunkCXYZ); - out.write(static_cast(chunkTIME), chunkSize(chunkTIME)); // <----4 TIME + location.write(static_cast(chunkTIME), chunkSize(chunkTIME)); // <----4 TIME free(chunkTIME); - out.write(static_cast(chunkSXYZ), chunkSize(chunkSXYZ)); // <----4 SXYZ + location.write(static_cast(chunkSXYZ), chunkSize(chunkSXYZ)); // <----4 SXYZ free(chunkSXYZ); - out.write(static_cast(chunkCRGE), chunkSize(chunkCRGE)); // <----4 CRGE + location.write(static_cast(chunkCRGE), chunkSize(chunkCRGE)); // <----4 CRGE free(chunkCRGE); - out.write(static_cast(chunkATPE), chunkSize(chunkATPE)); // <----4 CRGE + location.write(static_cast(chunkATPE), chunkSize(chunkATPE)); // <----4 CRGE free(chunkATPE); } @@ -260,11 +264,11 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, uxyz[idx++] = c.Y(); uxyz[idx++] = c.Z(); } - out.write(static_cast(chunkUGID), chunkSize(chunkUGID)); // + location.write(static_cast(chunkUGID), chunkSize(chunkUGID)); // free(chunkUGID); - out.write(static_cast(chunkUTIM), chunkSize(chunkUTIM)); // + location.write(static_cast(chunkUTIM), chunkSize(chunkUTIM)); // free(chunkUTIM); - out.write(static_cast(chunkUXYZ), chunkSize(chunkUXYZ)); // + location.write(static_cast(chunkUXYZ), chunkSize(chunkUXYZ)); // free(chunkUXYZ); } @@ -300,22 +304,22 @@ void VisualisationEventOpenGLSerializer::toFile(const VisualisationEvent& event, } } - out.write((char*)chunkCALO, chunkSize(chunkCALO)); // + location.write((char*)chunkCALO, chunkSize(chunkCALO)); // free(chunkCALO); - out.write((char*)chunkCALP, chunkSize(chunkCALP)); // + location.write((char*)chunkCALP, chunkSize(chunkCALP)); // free(chunkCALP); - out.write((char*)chunkCALG, chunkSize(chunkCALG)); // + location.write((char*)chunkCALG, chunkSize(chunkCALG)); // free(chunkCALG); - out.write((char*)chunkCALT, chunkSize(chunkCALT)); // + location.write((char*)chunkCALT, chunkSize(chunkCALT)); // free(chunkCALT); } { const auto chunkFINE = createChunk(FINE, 0); - out.write(static_cast(chunkFINE), chunkSize(chunkFINE)); // <----5 FINE + location.write(static_cast(chunkFINE), chunkSize(chunkFINE)); // <----5 FINE free(chunkFINE); } - out.close(); + location.close(); } void* VisualisationEventOpenGLSerializer::createChunk(const char* lbl, unsigned size) diff --git a/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx b/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx index 730af08b5fd61..8480e15ee9772 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx @@ -93,9 +93,14 @@ bool VisualisationEventROOTSerializer::existUInt64(TFile& f, const char* name) return true; } -void VisualisationEventROOTSerializer::toFile(const VisualisationEvent& event, std::string fileName) +void VisualisationEventROOTSerializer::toFile(const VisualisationEvent& event, Location& location) { + std::string fileName = location.fileName(); TFile f(fileName.c_str(), "recreate"); + if (f.IsZombie()) { + LOGF(error, "Could not create output file %s", fileName.c_str()); + return; + } saveInt("runNumber", event.mRunNumber); saveInt("runType", event.mRunType); diff --git a/EventVisualisation/DataConverter/src/converter.cxx b/EventVisualisation/DataConverter/src/converter.cxx index a0820d2e6feef..7bbada4bbb5e9 100644 --- a/EventVisualisation/DataConverter/src/converter.cxx +++ b/EventVisualisation/DataConverter/src/converter.cxx @@ -16,31 +16,40 @@ #include "EventVisualisationDataConverter/VisualisationEvent.h" #include "EventVisualisationView/Initializer.h" #include "EventVisualisationView/Options.h" +#include #include #include #include #include -#include #include -#include #include #include #include #include #include +#include +#include +#include + using namespace std::chrono_literals; // source file name, destination (not existing) file name, if limit > 0 then limit EACH type of data -int singleFileConversion(const std::string& src, const std::string& dst, const int limit = -1) +int singleFileConversion(const std::string& src, o2::event_visualisation::Location& dst, const int limit = -1) { - LOGF(info, "Translate: %s -> %s", src, dst); + LOGF(info, "Translate: %s -> %s", src, dst.fileName()); o2::event_visualisation::VisualisationEvent vEvent; auto srcSerializer = o2::event_visualisation::VisualisationEventSerializer::getInstance( std::filesystem::path(src).extension()); + auto dstExtension = std::filesystem::path( + src) + .extension(); // if there is no destination, there will be no extension change + if (!dst.fileName().empty()) { + dstExtension = std::filesystem::path(dst.fileName()).extension(); + } auto dstSerializer = o2::event_visualisation::VisualisationEventSerializer::getInstance( - std::filesystem::path(dst).extension()); + dstExtension); std::chrono::time_point currentTime = std::chrono::high_resolution_clock::now(); std::chrono::time_point endTime = std::chrono::high_resolution_clock::now(); @@ -61,8 +70,9 @@ int singleFileConversion(const std::string& src, const std::string& dst, const i // reads source folder files, find missing files in destination folder and convert them // source folder (/path-to-folder/.ext1) , destination folder (/path-to-folder/.ext2) -int folderConversion(const std::string& srcFolder, const std::string& dstFolder) +int folderConversion(const std::string& srcFolder, const o2::event_visualisation::Location& dstFolderLocation) { + const std::string dstFolder = dstFolderLocation.fileName(); std::vector supported = {".json", ".root", ".eve"}; auto ext1 = srcFolder.substr(srcFolder.rfind('.')); auto ext2 = dstFolder.substr(dstFolder.rfind('.')); @@ -109,7 +119,13 @@ int folderConversion(const std::string& srcFolder, const std::string& dstFolder) auto match = e.substr(0, e.size() - ext1.size()) + ext2; if (destinationList.end() == std::find(destinationList.begin(), destinationList.end(), match)) { // LOGF(info, "translate %s ->%s", src+e, dst+match); - singleFileConversion(src + e, dst + match); + o2::event_visualisation::Location location({.fileName = dst + match, + .port = dstFolderLocation.port(), + .host = dstFolderLocation.hostName()}); + singleFileConversion(src + e, location); + ; + singleFileConversion(src + e, location); + ; } } @@ -122,6 +138,10 @@ void my_handler(int s) exit(1); } +namespace po = boost::program_options; + +using namespace std; + int main(int argc, char** argv) { struct sigaction sigIntHandler { @@ -133,25 +153,59 @@ int main(int argc, char** argv) sigaction(SIGINT, &sigIntHandler, nullptr); LOGF(info, "Welcome in O2 event conversion tool"); - if (argc == 3) { - singleFileConversion(argv[1], argv[2]); // std::quick_exit(... - return 0; - } - if (argc == 4 and std::string(argv[1]) == std::string("-l")) { - singleFileConversion(argv[2], argv[3], 3); // std::quick_exit(... - return 0; - } - if (argc == 4 and std::string(argv[1]) == std::string("-f")) { - folderConversion(argv[2], argv[3]); // std::quick_exit(... - return 0; - } - if (argc == 4 and std::string(argv[1]) == std::string("-c")) { - while (true) { - std::this_thread::sleep_for(2000ms); - folderConversion(argv[2], argv[3]); + try { + int port; + string host; + int limit; + bool folderMode; + bool continuousMode; + vector sources; + po::options_description desc("Allowed options"); + desc.add_options()("help,h", "produce help message")("port", po::value(&port)->default_value(-1), "port number")("host", po::value(&host)->default_value("localhost"), "host name")("sources", po::value(&sources), "sources")("limit,l", po::value(&limit)->default_value(-1), "limit number of elements")("folder,f", po::bool_switch(&folderMode)->default_value(false), "convert folders")("continuous,c", po::bool_switch(&continuousMode)->default_value(false), "continuous folder mode"); + + po::positional_options_description p; + p.add("sources", 2); + + po::variables_map vm; + po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); + po::notify(vm); + + if (vm.count("help")) { + cout << desc << "\n"; + return 0; + } + + if (vm.count("sources")) { + if (vm["sources"].as>().size() != 2) { + cout << "two positional parameters expected" << "\n"; + return 0; + } } - return 0; + o2::event_visualisation::LocationParams locationParams; + locationParams.fileName = sources[1]; + locationParams.port = port; + locationParams.host = host; + + o2::event_visualisation::Location location(locationParams); + + if (folderMode) { + folderConversion(sources[0], location); + } else if (continuousMode) { + while (true) { + std::this_thread::sleep_for(2000ms); + folderConversion(sources[0], location); + } + } else { + singleFileConversion(sources[0], location, limit); + return 0; + } + } + + catch (exception& e) { + cerr << "error: " << e.what() << "\n"; + return 1; + } catch (...) { + cerr << "Exception of unknown type!\n"; } - LOGF(error, "two filename required, second should point to not existent file"); return -1; // std::quick_exit(-1); } diff --git a/EventVisualisation/View/src/EventManagerFrame.cxx b/EventVisualisation/View/src/EventManagerFrame.cxx index 6af49953d7e40..6c9796be94ee0 100644 --- a/EventVisualisation/View/src/EventManagerFrame.cxx +++ b/EventVisualisation/View/src/EventManagerFrame.cxx @@ -398,12 +398,12 @@ void EventManagerFrame::createOutreachScreenshot() if (skipCounter > 0) { skipCounter--; } else { - string fileName = this->mEventManager->getInstance().getDataSource()->getEventName(); + std::string fileName = this->mEventManager->getInstance().getDataSource()->getEventName(); if (fileName.size() < 5) { return; } - string imageFolder = ConfigurationManager::getScreenshotPath("outreach"); + std::string imageFolder = ConfigurationManager::getScreenshotPath("outreach"); if (!std::filesystem::is_directory(imageFolder)) { std::filesystem::create_directory(imageFolder); } diff --git a/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h b/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h index 1043ed5c303e0..6b3ec653c5350 100644 --- a/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h +++ b/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h @@ -183,7 +183,7 @@ class EveWorkflowHelper bool isInsideITSROF(float t); bool isInsideTimeBracket(float t); - void save(const std::string& jsonPath, const std::string& ext, int numberOfFiles); + void save(const std::string& jsonPath, const std::string& ext, int numberOfFiles, const std::string& receiverHostname, int receiverPort, int receiverTimeout, bool useOnlyFiles, bool useOnlySockets); bool mUseTimeBracket = false; bool mUseEtaBracketTPC = false; diff --git a/EventVisualisation/Workflow/include/EveWorkflow/FileProducer.h b/EventVisualisation/Workflow/include/EveWorkflow/FileProducer.h index d35ddcaa78711..e7203baceba2a 100644 --- a/EventVisualisation/Workflow/include/EveWorkflow/FileProducer.h +++ b/EventVisualisation/Workflow/include/EveWorkflow/FileProducer.h @@ -26,16 +26,15 @@ namespace event_visualisation class FileProducer { private: - size_t mFilesInFolder; std::string mPath; std::string mName; std::string mExt; public: - explicit FileProducer(const std::string& path, const std::string& ext, int filesInFolder = -1, - const std::string& name = "tracks_{timestamp}_{hostname}_{pid}{ext}"); + explicit FileProducer(const std::string& path, const std::string& ext, const std::string& name = "tracks_{timestamp}_{hostname}_{pid}{ext}"); [[nodiscard]] std::string newFileName() const; + void reduceNumberOfFiles(size_t filesInFolder) const; }; } // namespace event_visualisation diff --git a/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h b/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h index 1156d31d190ea..37d4155803e85 100644 --- a/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h +++ b/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h @@ -50,7 +50,8 @@ class TPCFastTransform; class O2DPLDisplaySpec : public o2::framework::Task { public: - static constexpr auto allowedTracks = "ITS,TPC,MFT,MCH,MID,ITS-TPC,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF,MCH-MID,MFT-MCH,MFT-MCH-MID,PHS,EMC,HMP"; + static constexpr auto allowedTracks = + "ITS,TPC,MFT,MCH,MID,ITS-TPC,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF,MCH-MID,MFT-MCH,MFT-MCH-MID,PHS,EMC,HMP"; static constexpr auto allowedClusters = "ITS,TPC,TRD,TOF,MFT,MCH,MID,PHS,EMC,HMP"; O2DPLDisplaySpec(bool disableWrite, bool useMC, o2::dataformats::GlobalTrackID::mask_t trkMask, @@ -60,12 +61,33 @@ class O2DPLDisplaySpec : public o2::framework::Task std::shared_ptr emcCalibLoader, const std::string& jsonPath, const std::string& ext, std::chrono::milliseconds timeInterval, - bool eveHostNameMatch) - : mDisableWrite(disableWrite), mUseMC(useMC), mTrkMask(trkMask), mClMask(clMask), mDataRequest(dataRequest), mGGCCDBRequest(gr), mEMCALCalibLoader(emcCalibLoader), mJsonPath(jsonPath), mExt(ext), mTimeInterval(timeInterval), mEveHostNameMatch(eveHostNameMatch), mRunType(o2::parameters::GRPECS::NONE) - + bool eveHostNameMatch, + const std::string& receiverHostname, + int receiverPort, + int receiverTimeout, + bool useOnlyFiles, + bool useOnlySockets) + : mDisableWrite(disableWrite), + mUseMC(useMC), + mTrkMask(trkMask), + mClMask(clMask), + mDataRequest(dataRequest), + mGGCCDBRequest(gr), + mEMCALCalibLoader(emcCalibLoader), + mJsonPath(jsonPath), + mExt(ext), + mTimeInterval(timeInterval), + mEveHostNameMatch(eveHostNameMatch), + mRunType(o2::parameters::GRPECS::NONE), + mReceiverHostname(receiverHostname), + mReceiverPort(receiverPort), + mReceiverTimeout(receiverTimeout), + mUseOnlyFiles(useOnlyFiles), + mUseOnlySockets(useOnlySockets) { this->mTimeStamp = std::chrono::high_resolution_clock::now() - timeInterval; // first run meets condition } + ~O2DPLDisplaySpec() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -81,7 +103,8 @@ class O2DPLDisplaySpec : public o2::framework::Task std::string mJsonPath; // folder where files are stored std::string mExt; // extension of created files (".json" or ".root") std::chrono::milliseconds mTimeInterval; // minimal interval between files in milliseconds - bool mPrimaryVertexTriggers; // instead of drawing vertices with tracks (and maybe calorimeter triggers), draw vertices with calorimeter triggers (and maybe tracks) + bool mPrimaryVertexTriggers; + // instead of drawing vertices with tracks (and maybe calorimeter triggers), draw vertices with calorimeter triggers (and maybe tracks) int mEventCounter = 0; std::chrono::time_point mTimeStamp; @@ -94,8 +117,13 @@ class O2DPLDisplaySpec : public o2::framework::Task std::shared_ptr mEMCALCalibLoader; std::unique_ptr mEMCALCalibrator; o2::tpc::VDriftHelper mTPCVDriftHelper{}; -}; + std::string mReceiverHostname; + int mReceiverPort; + int mReceiverTimeout; + bool mUseOnlyFiles; + bool mUseOnlySockets; +}; } // namespace o2::event_visualisation -#endif +#endif \ No newline at end of file diff --git a/EventVisualisation/Workflow/src/AO2DConverter.cxx b/EventVisualisation/Workflow/src/AO2DConverter.cxx index d339b150265de..f54907c20d260 100644 --- a/EventVisualisation/Workflow/src/AO2DConverter.cxx +++ b/EventVisualisation/Workflow/src/AO2DConverter.cxx @@ -74,8 +74,8 @@ struct AO2DConverter { mHelper->mEvent.setTfCounter(mTfCounter); mHelper->mEvent.setFirstTForbit(mTfOrbit); mHelper->mEvent.setCreationTime(collision.collisionTime()); - - mHelper->save(jsonPath, ".root", -1); + const std::string hostname("localhost"); + mHelper->save(jsonPath, ".root", -1, hostname, -1, 100, true, true); mHelper->clear(); } }; diff --git a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx index 8c795dd01c79f..2bb3c220d67a0 100644 --- a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx +++ b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx @@ -23,6 +23,7 @@ #include "ITStracking/IOUtils.h" #include "MFTTracking/IOUtils.h" #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "DataFormatsMID/Track.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsBase/Propagator.h" @@ -120,7 +121,8 @@ double EveWorkflowHelper::bcDiffToTFTimeMUS(const o2::InteractionRecord& ir) auto bcd = ir.differenceInBC(startIR); if (uint64_t(bcd) > o2::constants::lhc::LHCMaxBunches * 256 && BCDiffErrCount < MAXBCDiffErrCount) { - LOGP(alarm, "ATTENTION: wrong bunches diff. {} for current IR {} wrt 1st TF orbit {}", bcd, ir.asString(), startIR.asString()); + LOGP(alarm, "ATTENTION: wrong bunches diff. {} for current IR {} wrt 1st TF orbit {}", bcd, ir.asString(), + startIR.asString()); BCDiffErrCount++; } @@ -162,12 +164,14 @@ void EveWorkflowHelper::selectTracks(const CalibObjectsConst* calib, t0 *= this->mTPCBin2MUS; terr *= this->mTPCBin2MUS; } else if constexpr (isITSTrack()) { - t0 += 0.5f * this->mITSROFrameLengthMUS; // ITS time is supplied in \mus as beginning of ROF - terr *= this->mITSROFrameLengthMUS; // error is supplied as a half-ROF duration, convert to \mus - } else if constexpr (isMFTTrack()) { // Same for MFT + t0 += 0.5f * this->mITSROFrameLengthMUS; // ITS time is supplied in \mus as beginning of ROF + terr *= this->mITSROFrameLengthMUS; // error is supplied as a half-ROF duration, convert to \mus + } else if constexpr (isMFTTrack()) { + // Same for MFT t0 += 0.5f * this->mMFTROFrameLengthMUS; terr *= this->mMFTROFrameLengthMUS; - } else if constexpr (!(isMCHTrack() || isMIDTrack() || isGlobalFwdTrack())) { + } else if constexpr (!(isMCHTrack() || isMIDTrack() || + isGlobalFwdTrack())) { // for all other tracks the time is in \mus with gaussian error terr *= mPVParams->nSigmaTimeTrack; // gaussian errors must be scaled by requested n-sigma } @@ -197,7 +201,8 @@ void EveWorkflowHelper::selectTracks(const CalibObjectsConst* calib, } } }; - auto creator = [&conf, maskTrk, this, &correctTrackTime, &flagTime, &fixMFTMCHMIDLabel](auto& trk, GID gid, float time, float terr) { + auto creator = [&conf, maskTrk, this, &correctTrackTime, &flagTime, &fixMFTMCHMIDLabel](auto& trk, GID gid, + float time, float terr) { fixMFTMCHMIDLabel(gid); const auto src = gid.getSource(); @@ -234,7 +239,8 @@ void EveWorkflowHelper::selectTracks(const CalibObjectsConst* calib, bool checkTPCDCA = conf.TPCOnlyMaxDCARZ[0] > 0.f || conf.TPCOnlyMaxDCARZ[1] > 0.f; const auto trackIndex = mRecoCont->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks const auto vtxRefs = mRecoCont->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs - const auto totalPrimaryVertices = vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them + const auto totalPrimaryVertices = + vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them for (std::size_t iv = 0; iv < totalPrimaryVertices; iv++) { const auto& pv = mRecoCont->getPrimaryVertex(iv); @@ -259,15 +265,19 @@ void EveWorkflowHelper::selectTracks(const CalibObjectsConst* calib, if (gid.getSource() == o2::dataformats::GlobalTrackID::TPC && checkTPCDCA) { const auto& tpcTr = mRecoCont->getTPCTrack(gid); o2::track::TrackPar trc{tpcTr}; - if (!tpcTr.hasBothSidesClusters()) { // need to correct track Z with this vertex time - float dz = (tpcTr.getTime0() * mTPCTimeBins2MUS - (pv.getTimeStamp().getTimeStamp() + mTPCVDrift->getTimeOffset())) * mTPCVDrift->getVDrift(); + if (!tpcTr.hasBothSidesClusters()) { + // need to correct track Z with this vertex time + float dz = (tpcTr.getTime0() * mTPCTimeBins2MUS - + (pv.getTimeStamp().getTimeStamp() + mTPCVDrift->getTimeOffset())) * + mTPCVDrift->getVDrift(); if (tpcTr.hasCSideClustersOnly()) { dz = -dz; } trc.setZ(trc.getZ() + dz); } std::array dca; - if (!prop->propagateToDCA(pvXYZ, trc, prop->getNominalBz(), 10., o2::base::PropagatorF::MatCorrType::USEMatCorrNONE, &dca) || + if (!prop->propagateToDCA(pvXYZ, trc, prop->getNominalBz(), 10., + o2::base::PropagatorF::MatCorrType::USEMatCorrNONE, &dca) || (conf.TPCOnlyMaxDCARZ[1] > 0. && std::abs(dca[1]) > conf.TPCOnlyMaxDCARZ[1]) || (conf.TPCOnlyMaxDCARZ[0] > 0. && std::abs(dca[0]) > conf.TPCOnlyMaxDCARZ[0])) { continue; @@ -303,7 +313,8 @@ void EveWorkflowHelper::selectTowers() if (conf.PVMode) { const auto trackIndex = mRecoCont->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks const auto vtxRefs = mRecoCont->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs - const auto totalPrimaryVertices = vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them + const auto totalPrimaryVertices = + vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them for (std::size_t iv = 0; iv < totalPrimaryVertices; iv++) { const auto& vtref = vtxRefs[iv]; @@ -315,7 +326,8 @@ void EveWorkflowHelper::selectTowers() mPrimaryVertexTriggerGIDs[iv].emplace_back(GID{static_cast(i), GID::HMP}); } - const auto triggersPHOS = gsl::span(trackIndex.data() + vtref.getFirstEntryOfSource(GID::PHS), vtref.getEntriesOfSource(GID::PHS)); + const auto triggersPHOS = gsl::span(trackIndex.data() + vtref.getFirstEntryOfSource(GID::PHS), + vtref.getEntriesOfSource(GID::PHS)); for (const auto& tvid : triggersPHOS) { mTotalDataTypes[GID::PHS]++; @@ -327,7 +339,8 @@ void EveWorkflowHelper::selectTowers() } } - const auto triggersEMCAL = gsl::span(trackIndex.data() + vtref.getFirstEntryOfSource(GID::EMC), vtref.getEntriesOfSource(GID::EMC)); + const auto triggersEMCAL = gsl::span(trackIndex.data() + vtref.getFirstEntryOfSource(GID::EMC), + vtref.getEntriesOfSource(GID::EMC)); for (const auto& tvid : triggersEMCAL) { mTotalDataTypes[GID::EMC]++; @@ -416,11 +429,14 @@ void EveWorkflowHelper::draw(std::size_t primaryVertexIdx, bool sortTracks) break; case GID::TPC: { float dz = 0.f; - if (conf.PVMode) { // for TPC the nominal time (center of the bracket) is stored but in the PVMode we correct it by the PV time + if (conf.PVMode) { + // for TPC the nominal time (center of the bracket) is stored but in the PVMode we correct it by the PV time tim = pvTime; const auto& tpcTr = mRecoCont->getTPCTrack(gid); - if (!tpcTr.hasBothSidesClusters()) { // need to correct track Z with this vertex time - float dz = (tpcTr.getTime0() * mTPCTimeBins2MUS - (pvTime + mTPCVDrift->getTimeOffset())) * mTPCVDrift->getVDrift(); + if (!tpcTr.hasBothSidesClusters()) { + // need to correct track Z with this vertex time + float dz = (tpcTr.getTime0() * mTPCTimeBins2MUS - (pvTime + mTPCVDrift->getTimeOffset())) * + mTPCVDrift->getVDrift(); if (tpcTr.hasCSideClustersOnly()) { dz = -dz; } @@ -500,14 +516,25 @@ void EveWorkflowHelper::draw(std::size_t primaryVertexIdx, bool sortTracks) } } -void EveWorkflowHelper::save(const std::string& jsonPath, const std::string& ext, int numberOfFiles) +void EveWorkflowHelper::save(const std::string& jsonPath, const std::string& ext, int numberOfFiles, + const std::string& receiverHostname, int receiverPort, int receiverTimeout, bool useOnlyFiles, + bool useOnlySockets) { mEvent.setEveVersion(o2_eve_version); - FileProducer producer(jsonPath, ext, numberOfFiles); - VisualisationEventSerializer::getInstance(ext)->toFile(mEvent, producer.newFileName()); + FileProducer producer(jsonPath, ext); + producer.reduceNumberOfFiles(numberOfFiles); + Location location({.fileName = producer.newFileName(), + .port = receiverPort, + .timeout = receiverTimeout, + .host = receiverHostname, + .toFile = !useOnlySockets, + .toSocket = !useOnlyFiles}); + VisualisationEventSerializer::getInstance(ext)->toFile(mEvent, location); } -std::vector EveWorkflowHelper::getTrackPoints(const o2::track::TrackPar& trc, float minR, float maxR, float maxStep, float minZ, float maxZ) +std::vector + EveWorkflowHelper::getTrackPoints(const o2::track::TrackPar& trc, float minR, float maxR, float maxStep, float minZ, + float maxZ) { // adjust minR according to real track start from track starting point auto maxR2 = maxR * maxR; @@ -528,7 +555,8 @@ std::vector EveWorkflowHelper::getTrackPoints(const o2::track::TrackPar& tr auto tp = trc; float dxmin = std::abs(xMin - tp.getX()), dxmax = std::abs(xMax - tp.getX()); - if (dxmin > dxmax) { // start from closest end + if (dxmin > dxmax) { + // start from closest end std::swap(xMin, xMax); dx = -dx; } @@ -556,7 +584,8 @@ std::vector EveWorkflowHelper::getTrackPoints(const o2::track::TrackPar& tr return pnts; } -void EveWorkflowHelper::addTrackToEvent(const o2::track::TrackPar& tr, GID gid, float trackTime, float dz, GID::Source source, float maxStep) +void EveWorkflowHelper::addTrackToEvent(const o2::track::TrackPar& tr, GID gid, float trackTime, float dz, + GID::Source source, float maxStep) { if (source == GID::NSources) { source = (o2::dataformats::GlobalTrackID::Source)gid.getSource(); @@ -599,7 +628,8 @@ void EveWorkflowHelper::prepareITSClusters(const o2::itsmft::TopologyDictionary* } } -void EveWorkflowHelper::prepareMFTClusters(const o2::itsmft::TopologyDictionary* dict) // do we also have something as ITS...dict? +void EveWorkflowHelper::prepareMFTClusters( + const o2::itsmft::TopologyDictionary* dict) // do we also have something as ITS...dict? { const auto& MFTClusterROFRec = this->mRecoCont->getMFTClustersROFRecords(); const auto& clusMFT = this->mRecoCont->getMFTClusters(); @@ -669,7 +699,8 @@ void EveWorkflowHelper::drawEMC(GID gid) const auto& conf = EveConfParam::Instance(); for (const auto& cell : cellsForTrigger) { - if (!(cell.getType() == o2::emcal::ChannelType_t::HIGH_GAIN || cell.getType() == o2::emcal::ChannelType_t::LOW_GAIN)) { + if (!(cell.getType() == o2::emcal::ChannelType_t::HIGH_GAIN || + cell.getType() == o2::emcal::ChannelType_t::LOW_GAIN)) { // Select FEE cells (excluding LEDMON or TRU cells) continue; } @@ -738,7 +769,7 @@ void EveWorkflowHelper::drawTPCTRD(GID gid, float trackTime, GID::Source source) const auto& tpcTrdTrack = mRecoCont->getTPCTRDTrack(gid); addTrackToEvent(tpcTrdTrack, gid, trackTime, 0., source); drawTPCClusters(tpcTrdTrack.getRefGlobalTrackId(), trackTime * mMUS2TPCTimeBins); - drawTRDClusters(tpcTrdTrack); // tracktime + drawTRDClusters(tpcTrdTrack); // tracktime } void EveWorkflowHelper::drawITSTPCTRD(GID gid, float trackTime, GID::Source source) @@ -774,7 +805,7 @@ void EveWorkflowHelper::drawTPCTOF(GID gid, float trackTime) const auto& match = mRecoCont->getTPCTOFMatch(gid.getIndex()); addTrackToEvent(trTPCTOF, gid, trackTime, 0); drawTPCClusters(match.getTrackRef(), trackTime * mMUS2TPCTimeBins); - drawTOFClusters(gid); // trackTime + drawTOFClusters(gid); // trackTime } void EveWorkflowHelper::drawMFTMCH(GID gid, float trackTime) @@ -832,8 +863,9 @@ void EveWorkflowHelper::drawMCHMID(GID gid, float trackTime) void EveWorkflowHelper::drawAODBarrel(EveWorkflowHelper::AODBarrelTrack const& track, float trackTime) { - const std::array arraypar = {track.y(), track.z(), track.snp(), - track.tgl(), track.signed1Pt()}; + const std::array arraypar = { + track.y(), track.z(), track.snp(), + track.tgl(), track.signed1Pt()}; const auto tr = o2::track::TrackPar(track.x(), track.alpha(), arraypar); @@ -907,7 +939,9 @@ void EveWorkflowHelper::drawForwardTrack(GID gid, mch::TrackParam track, float s auto vTrack = mEvent.addTrack({.time = static_cast(trackTime), .charge = 0, .PID = o2::track::PID::Muon, - .startXYZ = {(float)track.getNonBendingCoor(), (float)track.getBendingCoor(), (float)track.getZ()}, + .startXYZ = { + (float)track.getNonBendingCoor(), (float)track.getBendingCoor(), + (float)track.getZ()}, .phi = (float)0, .theta = (float)0, .eta = (float)0, @@ -950,7 +984,8 @@ void EveWorkflowHelper::drawTOFClusters(GID gid) void EveWorkflowHelper::drawITSClusters(GID gid) // float trackTime { - if (gid.getSource() == GID::ITS) { // this is for for full standalone tracks + if (gid.getSource() == GID::ITS) { + // this is for for full standalone tracks const auto& trc = mRecoCont->getITSTrack(gid); auto refs = mRecoCont->getITSTracksClusterRefs(); int ncl = trc.getNumberOfClusters(); @@ -961,7 +996,8 @@ void EveWorkflowHelper::drawITSClusters(GID gid) // float trackTime float xyz[] = {glo.X(), glo.Y(), glo.Z()}; drawPoint(xyz); // trackTime; } - } else if (gid.getSource() == GID::ITSAB) { // this is for ITS tracklets from ITS-TPC afterburner + } else if (gid.getSource() == GID::ITSAB) { + // this is for ITS tracklets from ITS-TPC afterburner const auto& trc = mRecoCont->getITSABRef(gid); const auto& refs = mRecoCont->getITSABClusterRefs(); int ncl = trc.getNClusters(); @@ -990,9 +1026,12 @@ void EveWorkflowHelper::drawTPCClusters(GID gid, float trackTimeTB) const auto& clTPC = trc.getCluster(mTPCTracksClusIdx, iCl, *mTPCClusterIdxStruct, sector, row); std::array xyz; - this->mTPCFastTransform->TransformIdeal(sector, row, clTPC.getPad(), clTPC.getTime(), xyz[0], xyz[1], xyz[2], trackTimeTB); // in sector coordinate - o2::math_utils::rotateZ(xyz, o2::math_utils::sector2Angle(sector % o2::tpc::SECTORSPERSIDE)); // lab coordinate (global) - mEvent.addCluster(xyz.data()); // trackTimeTB / mMUS2TPCTimeBins + this->mTPCFastTransform->TransformIdeal(sector, row, clTPC.getPad(), clTPC.getTime(), xyz[0], xyz[1], xyz[2], + trackTimeTB); // in sector coordinate + o2::math_utils::rotateZ(xyz, o2::math_utils::sector2Angle( + sector % o2::tpc::SECTORSPERSIDE)); // lab coordinate (global) + mEvent.addCluster( + xyz.data()); // trackTimeTB / mMUS2TPCTimeBins } } @@ -1018,8 +1057,10 @@ void EveWorkflowHelper::drawTPC(GID gid, float trackTime, float dz) } addTrackToEvent(tr, gid, trackTime, dz, GID::TPC); - float clTime0 = EveConfParam::Instance().PVMode ? trackTime * mMUS2TPCTimeBins : -2e9; // in PVMode use supplied real time converted to TB, otherwise pass dummy time to use tpcTrack.getTime0 - drawTPCClusters(gid, clTime0); // trackTime + float clTime0 = EveConfParam::Instance().PVMode + ? trackTime * mMUS2TPCTimeBins + : -2e9; // in PVMode use supplied real time converted to TB, otherwise pass dummy time to use tpcTrack.getTime0 + drawTPCClusters(gid, clTime0); // trackTime } void EveWorkflowHelper::drawITS(GID gid, float trackTime) @@ -1074,7 +1115,9 @@ void EveWorkflowHelper::drawMID(GID gid, float trackTime) auto vTrack = mEvent.addTrack({.time = static_cast(trackTime), .charge = (int)0, .PID = o2::track::PID::Muon, - .startXYZ = {(float)midTrack.getPositionX(), (float)midTrack.getPositionY(), (float)midTrack.getPositionZ()}, + .startXYZ = { + (float)midTrack.getPositionX(), (float)midTrack.getPositionY(), + (float)midTrack.getPositionZ()}, .phi = (float)0, .theta = (float)0, .eta = (float)0, @@ -1149,9 +1192,12 @@ EveWorkflowHelper::EveWorkflowHelper() } o2::mch::TrackExtrap::setField(); this->mMFTGeom = o2::mft::GeometryTGeo::Instance(); - this->mMFTGeom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + this->mMFTGeom->fillMatrixCache( + o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); this->mITSGeom = o2::its::GeometryTGeo::Instance(); - this->mITSGeom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::L2G)); + this->mITSGeom->fillMatrixCache( + o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); this->mEMCALGeom = o2::emcal::Geometry::GetInstance(""); this->mPHOSGeom = o2::phos::Geometry::GetInstance(""); this->mTPCFastTransform = (o2::tpc::TPCFastTransformHelperO2::instance()->create(0)); @@ -1161,10 +1207,18 @@ EveWorkflowHelper::EveWorkflowHelper() mTPCBin2MUS = elParams.ZbinWidth; const auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); const auto& alpParamsITS = o2::itsmft::DPLAlpideParam::Instance(); - mITSROFrameLengthMUS = grp->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsITS.roFrameLengthTrig * 1.e-3; + mITSROFrameLengthMUS = grp->isDetContinuousReadOut(o2::detectors::DetID::ITS) + ? alpParamsITS.roFrameLengthInBC * + o2::constants::lhc::LHCBunchSpacingMUS + : alpParamsITS.roFrameLengthTrig * + 1.e-3; const auto& alpParamsMFT = o2::itsmft::DPLAlpideParam::Instance(); - mMFTROFrameLengthMUS = grp->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsMFT.roFrameLengthTrig * 1.e-3; + mMFTROFrameLengthMUS = grp->isDetContinuousReadOut(o2::detectors::DetID::MFT) + ? alpParamsMFT.roFrameLengthInBC * + o2::constants::lhc::LHCBunchSpacingMUS + : alpParamsMFT.roFrameLengthTrig * + 1.e-3; mPVParams = &o2::vertexing::PVertexerParams::Instance(); @@ -1177,7 +1231,8 @@ void EveWorkflowHelper::setTPCVDrift(const o2::tpc::VDriftCorrFact* v) { mTPCVDrift = v; if (v) { - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mTPCFastTransform.get(), 0, v->corrFact, v->refVDrift, v->getTimeOffset()); + o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mTPCFastTransform.get(), 0, v->corrFact, + v->refVDrift, v->getTimeOffset()); } } @@ -1198,11 +1253,14 @@ GID::Source EveWorkflowHelper::detectorMapToGIDSource(uint8_t dm) return GID::TPCTOF; case static_cast(o2::aod::track::TPC) | static_cast(o2::aod::track::TRD): return GID::TPCTRD; - case static_cast(o2::aod::track::ITS) | static_cast(o2::aod::track::TPC) | static_cast(o2::aod::track::TRD): + case static_cast(o2::aod::track::ITS) | static_cast(o2::aod::track::TPC) | + static_cast(o2::aod::track::TRD): return GID::ITSTPCTRD; - case static_cast(o2::aod::track::ITS) | static_cast(o2::aod::track::TPC) | static_cast(o2::aod::track::TOF): + case static_cast(o2::aod::track::ITS) | static_cast(o2::aod::track::TPC) | + static_cast(o2::aod::track::TOF): return GID::ITSTPCTOF; - case static_cast(o2::aod::track::TPC) | static_cast(o2::aod::track::TRD) | static_cast(o2::aod::track::TOF): + case static_cast(o2::aod::track::TPC) | static_cast(o2::aod::track::TRD) | + static_cast(o2::aod::track::TOF): return GID::TPCTRDTOF; default: return GID::ITSTPCTRDTOF; diff --git a/EventVisualisation/Workflow/src/FileProducer.cxx b/EventVisualisation/Workflow/src/FileProducer.cxx index c50dcb8fad135..01e68240bfe6a 100644 --- a/EventVisualisation/Workflow/src/FileProducer.cxx +++ b/EventVisualisation/Workflow/src/FileProducer.cxx @@ -30,13 +30,18 @@ using std::chrono::duration_cast; using std::chrono::milliseconds; using std::chrono::system_clock; -FileProducer::FileProducer(const std::string& path, const std::string& ext, int filesInFolder, const std::string& name) +FileProducer::FileProducer(const std::string& path, const std::string& ext, const std::string& name) { - this->mFilesInFolder = filesInFolder; this->mPath = path; this->mName = name; this->mExt = ext; - o2::utils::createDirectoriesIfAbsent(path); // create folder if not exists (fails if no rights) + o2::utils::createDirectoriesIfAbsent(path); // create a folder if not exists (fails if no rights) +} + +void FileProducer::reduceNumberOfFiles(size_t filesInFolder) const +{ + const std::vector ext = {".json", ".root", ".eve"}; + DirectoryLoader::reduceNumberOfFiles(this->mPath, DirectoryLoader::load(this->mPath, "_", ext), filesInFolder); } std::string FileProducer::newFileName() const @@ -52,8 +57,5 @@ std::string FileProducer::newFileName() const fmt::arg("pid", pid), fmt::arg("timestamp", millisec_since_epoch), fmt::arg("ext", this->mExt)); - const std::vector ext = {".json", ".root", ".eve"}; - DirectoryLoader::reduceNumberOfFiles(this->mPath, DirectoryLoader::load(this->mPath, "_", ext), this->mFilesInFolder); - return this->mPath + "/" + result; } diff --git a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx index e02e1ee20ce58..bd8ab5a664d99 100644 --- a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx +++ b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx @@ -60,6 +60,11 @@ void customize(std::vector& workflowOptions) { std::vector options{ {"jsons-folder", VariantType::String, "jsons", {"name of the folder to store json files"}}, + {"receiver-hostname", VariantType::String, "arcbs04.cern.ch", {"name of the host where visualisation data is transmitted (only eve format)"}}, + {"receiver-port", VariantType::Int, 8001, {"port number of the host where visualisation data is transmitted (only eve format)"}}, + {"receiver-timeout", VariantType::Int, 300, {"socket connection timeout (ms)"}}, + {"use-only-files", VariantType::Bool, false, {"do not transmit visualisation data using sockets (only eve format)"}}, + {"use-only-sockets", VariantType::Bool, false, {"do not store visualisation data using filesystem"}}, {"use-json-format", VariantType::Bool, false, {"instead of eve format (default) use json format"}}, {"use-root-format", VariantType::Bool, false, {"instead of eve format (default) use root format"}}, {"eve-hostname", VariantType::String, "", {"name of the host allowed to produce files (empty means no limit)"}}, @@ -186,7 +191,7 @@ void O2DPLDisplaySpec::run(ProcessingContext& pc) helper.mEvent.setRunType(this->mRunType); helper.mEvent.setPrimaryVertex(pv); helper.mEvent.setCreationTime(tinfo.creation); - helper.save(this->mJsonPath, this->mExt, conf.maxFiles); + helper.save(this->mJsonPath, this->mExt, conf.maxFiles, this->mReceiverHostname, this->mReceiverPort, this->mReceiverTimeout, this->mUseOnlyFiles, this->mUseOnlySockets); filesSaved++; currentTime = std::chrono::high_resolution_clock::now(); // time AFTER save this->mTimeStamp = currentTime; // next run AFTER period counted from last save @@ -302,6 +307,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) bool useMC = !cfgc.options().get("disable-mc"); bool disableWrite = cfgc.options().get("disable-write"); + auto receiverHostname = cfgc.options().get("receiver-hostname"); + auto receiverPort = cfgc.options().get("receiver-port"); + auto receiverTimeout = cfgc.options().get("receiver-timeout"); + auto useOnlyFiles = cfgc.options().get("use-only-files"); + auto useOnlySockets = cfgc.options().get("use-only-sockets"); + char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); bool eveHostNameMatch = eveHostName.empty() || eveHostName == hostname; @@ -398,7 +409,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) "o2-eve-export", dataRequest->inputs, {}, - AlgorithmSpec{adaptFromTask(disableWrite, useMC, srcTrk, srcCl, dataRequest, ggRequest, emcalCalibLoader, jsonFolder, ext, timeInterval, eveHostNameMatch)}}); + AlgorithmSpec{adaptFromTask(disableWrite, useMC, srcTrk, srcCl, dataRequest, ggRequest, + emcalCalibLoader, jsonFolder, ext, timeInterval, eveHostNameMatch, + receiverHostname, receiverPort, receiverTimeout, useOnlyFiles, useOnlySockets)}}); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(cfgc, specs); diff --git a/Framework/AODMerger/src/aodMerger.cxx b/Framework/AODMerger/src/aodMerger.cxx index 2be87274a928f..3ea45e84a39e0 100644 --- a/Framework/AODMerger/src/aodMerger.cxx +++ b/Framework/AODMerger/src/aodMerger.cxx @@ -13,6 +13,7 @@ #include #include #include +#include #include "TSystem.h" #include "TFile.h" diff --git a/Framework/AODMerger/src/aodStrainer.cxx b/Framework/AODMerger/src/aodStrainer.cxx index fc54aa9c533cf..26af1205f0c35 100644 --- a/Framework/AODMerger/src/aodStrainer.cxx +++ b/Framework/AODMerger/src/aodStrainer.cxx @@ -15,6 +15,7 @@ #include #include #include +#include #include "TSystem.h" #include "TFile.h" diff --git a/Framework/AODMerger/src/aodThinner.cxx b/Framework/AODMerger/src/aodThinner.cxx index e724595b94ba6..9b03ebf2f360f 100644 --- a/Framework/AODMerger/src/aodThinner.cxx +++ b/Framework/AODMerger/src/aodThinner.cxx @@ -11,6 +11,8 @@ #include #include +#include +#include #include "TSystem.h" #include "TStopwatch.h" diff --git a/Framework/AnalysisSupport/CMakeLists.txt b/Framework/AnalysisSupport/CMakeLists.txt index dedbf8cb590b2..6024134a5495d 100644 --- a/Framework/AnalysisSupport/CMakeLists.txt +++ b/Framework/AnalysisSupport/CMakeLists.txt @@ -16,9 +16,16 @@ if(TARGET JAliEn::JAliEn) set(EXTRA_TARGETS XRootD::Client JAliEn::JAliEn) endif() +o2_add_library(FrameworkOnDemandTablesSupport + SOURCES src/OnDemandPlugin.cxx + src/AODReaderHelpers.cxx + PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src + PUBLIC_LINK_LIBRARIES O2::Framework ${EXTRA_TARGETS}) + o2_add_library(FrameworkAnalysisSupport SOURCES src/Plugin.cxx src/DataInputDirector.cxx + src/TableTreeHelpers.cxx src/AODJAlienReaderHelpers.cxx src/AODWriterHelpers.cxx PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src @@ -39,3 +46,9 @@ o2_add_test(DataInputDirector NAME test_Framework_test_DataInputDirector COMPONENT_NAME Framework LABELS framework PUBLIC_LINK_LIBRARIES O2::FrameworkAnalysisSupport) + +o2_add_test(TableToTree NAME benchmark_TableToTree + SOURCES test/benchmark_TableToTree.cxx + COMPONENT_NAME Framework + LABELS framework + PUBLIC_LINK_LIBRARIES O2::FrameworkAnalysisSupport benchmark::benchmark) diff --git a/Framework/Core/include/Framework/TableTreeHelpers.h b/Framework/AnalysisSupport/include/Framework/TableTreeHelpers.h similarity index 76% rename from Framework/Core/include/Framework/TableTreeHelpers.h rename to Framework/AnalysisSupport/include/Framework/TableTreeHelpers.h index 3f76298a5bbd4..c5e9d5fa14261 100644 --- a/Framework/Core/include/Framework/TableTreeHelpers.h +++ b/Framework/AnalysisSupport/include/Framework/TableTreeHelpers.h @@ -18,7 +18,7 @@ #include "TTreeReader.h" #include "TTreeReaderValue.h" #include "TTreeReaderArray.h" -#include "TableBuilder.h" +#include "Framework/TableBuilder.h" #include #include @@ -91,30 +91,6 @@ class TableToTree std::vector> mColumnReaders; }; -class FragmentToBatch -{ - public: - // The function to be used to create the required stream. - using StreamerCreator = std::function(std::shared_ptr, const std::shared_ptr& buffer)>; - - FragmentToBatch(StreamerCreator, std::shared_ptr, arrow::MemoryPool* pool = arrow::default_memory_pool()); - void setLabel(const char* label); - void fill(std::shared_ptr dataSetSchema, std::shared_ptr); - std::shared_ptr finalize(); - - std::shared_ptr streamer(std::shared_ptr buffer) - { - return mCreator(mFragment, buffer); - } - - private: - std::shared_ptr mFragment; - arrow::MemoryPool* mArrowMemoryPool = nullptr; - std::string mTableLabel; - std::shared_ptr mRecordBatch; - StreamerCreator mCreator; -}; - // ----------------------------------------------------------------------------- } // namespace o2::framework diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx index 85ed9cd573d8a..cde6c85f2c624 100644 --- a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx @@ -98,29 +98,6 @@ using o2::monitoring::tags::Value; namespace o2::framework::readers { -auto setEOSCallback(InitContext& ic) -{ - ic.services().get().set( - [](EndOfStreamContext& eosc) { - auto& control = eosc.services().get(); - control.endOfStream(); - control.readyToQuit(QuitRequest::Me); - }); -} - -template -static inline auto extractTypedOriginal(ProcessingContext& pc) -{ - /// FIXME: this should be done in invokeProcess() as some of the originals may be compound tables - return O{pc.inputs().get(aod::MetadataTrait::metadata::tableLabel())->asArrowTable()}; -} - -template -static inline auto extractOriginalsTuple(framework::pack, ProcessingContext& pc) -{ - return std::make_tuple(extractTypedOriginal(pc)...); -} - AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const& ctx) { // aod-parent-base-path-replacement is now a workflow option, so it needs to be @@ -145,6 +122,7 @@ AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const stats.updateStats({static_cast(ProcessingStatsId::ARROW_BYTES_DESTROYED), DataProcessingStats::Op::Set, 0}); stats.updateStats({static_cast(ProcessingStatsId::ARROW_MESSAGES_DESTROYED), DataProcessingStats::Op::Set, 0}); stats.updateStats({static_cast(ProcessingStatsId::ARROW_BYTES_EXPIRED), DataProcessingStats::Op::Set, 0}); + stats.updateStats({static_cast(ProcessingStatsId::CONSUMED_TIMEFRAMES), DataProcessingStats::Op::Set, 0}); if (!options.isSet("aod-file-private")) { LOGP(fatal, "No input file defined!"); @@ -198,7 +176,7 @@ AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const numTF, watchdog, maxRate, - didir, reportTFN, reportTFFileName](Monitoring& monitoring, DataAllocator& outputs, ControlService& control, DeviceSpec const& device) { + didir, reportTFN, reportTFFileName](Monitoring& monitoring, DataAllocator& outputs, ControlService& control, DeviceSpec const& device, DataProcessingStats& dpstats) { // Each parallel reader device.inputTimesliceId reads the files fileCounter*device.maxInputTimeslices+device.inputTimesliceId // the TF to read is numTF assert(device.inputTimesliceId < device.maxInputTimeslices); @@ -301,6 +279,10 @@ AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const } } totalDFSent++; + + // Use the new API for sending TIMESLICE_NUMBER_STARTED + dpstats.updateStats({(int)ProcessingStatsId::TIMESLICE_NUMBER_STARTED, DataProcessingStats::Op::Add, 1}); + dpstats.processCommandQueue(); monitoring.send(Metric{(uint64_t)totalDFSent, "df-sent"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); monitoring.send(Metric{(uint64_t)totalSizeUncompressed / 1000, "aod-bytes-read-uncompressed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); monitoring.send(Metric{(uint64_t)totalSizeCompressed / 1000, "aod-bytes-read-compressed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx new file mode 100644 index 0000000000000..4c1c065000186 --- /dev/null +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -0,0 +1,202 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "AODReaderHelpers.h" +#include "../src/ExpressionJSONHelpers.h" +#include "../src/IndexJSONHelpers.h" + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/DataProcessingHelpers.h" +#include "Framework/AlgorithmSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/DataSpecViews.h" +#include "Framework/ConfigContext.h" +#include "Framework/DanglingEdgesContext.h" + +namespace o2::framework::readers +{ +namespace +{ +struct Buildable { + bool exclusive = false; + std::string binding; + std::vector labels; + std::vector matchers; + header::DataOrigin origin; + header::DataDescription description; + header::DataHeader::SubSpecificationType version; + std::vector records; + std::shared_ptr outputSchema; + + explicit Buildable(InputSpec const& spec) + : binding{spec.binding} + { + auto&& [origin_, description_, version_] = DataSpecUtils::asConcreteDataMatcher(spec); + origin = origin_; + description = description_; + version = version_; + + auto loc = std::find_if(spec.metadata.begin(), spec.metadata.end(), [](ConfigParamSpec const& cps) { return cps.name.compare("index-records") == 0; }); + std::stringstream iws(loc->defaultValue.get()); + records = IndexJSONHelpers::read(iws); + + loc = std::find_if(spec.metadata.begin(), spec.metadata.end(), [](ConfigParamSpec const& cps) { return cps.name.compare("index-exclusive") == 0; }); + exclusive = loc->defaultValue.get(); + + for (auto const& r : records) { + labels.emplace_back(r.label); + matchers.emplace_back(r.matcher); + } + outputSchema = std::make_shared([](std::vector const& recs) { + std::vector> fields; + fields.reserve(recs.size()); + std::ranges::transform(recs, std::back_inserter(fields), [](auto& r) { return r.field(); }); + return fields; + }(records)) + ->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{binding}})); + } + + framework::Builder createBuilder() const + { + return { + exclusive, + labels, + matchers, + records, + outputSchema, + origin, + description, + version, + nullptr}; + } +}; + +} // namespace + +AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& /*ctx*/) +{ + return AlgorithmSpec::InitCallback{[](InitContext& ic) { + auto const& requested = ic.services().get().requestedIDXs; + std::vector builders; + builders.reserve(requested.size()); + std::ranges::transform(requested, std::back_inserter(builders), [](auto const& i) { return Buildable{i}.createBuilder(); }); + return [builders](ProcessingContext& pc) mutable { + auto outputs = pc.outputs(); + std::ranges::for_each(builders, [&pc, &outputs](auto& builder) { outputs.adopt(Output{builder.origin, builder.description, builder.version}, builder.materialize(pc)); }); + }; + }}; +} + +namespace +{ +struct Spawnable { + std::string binding; + std::vector labels; + std::vector matchers; + std::vector projectors; + std::vector> expressions; + std::shared_ptr outputSchema; + std::shared_ptr inputSchema; + + header::DataOrigin origin; + header::DataDescription description; + header::DataHeader::SubSpecificationType version; + + explicit Spawnable(InputSpec const& spec) + : binding{spec.binding} + { + auto&& [origin_, description_, version_] = DataSpecUtils::asConcreteDataMatcher(spec); + origin = origin_; + description = description_; + version = version_; + auto loc = std::find_if(spec.metadata.begin(), spec.metadata.end(), [](ConfigParamSpec const& cps) { return cps.name.compare("projectors") == 0; }); + std::stringstream iws(loc->defaultValue.get()); + projectors = ExpressionJSONHelpers::read(iws); + + loc = std::find_if(spec.metadata.begin(), spec.metadata.end(), [](ConfigParamSpec const& cps) { return cps.name.compare("schema") == 0; }); + iws.clear(); + iws.str(loc->defaultValue.get()); + outputSchema = ArrowJSONHelpers::read(iws); + o2::framework::addLabelToSchema(outputSchema, binding.c_str()); + + std::vector> schemas; + for (auto const& i : spec.metadata | views::filter_string_params_starts_with("input-schema:")) { + labels.emplace_back(i.name.substr(13)); + iws.clear(); + auto json = i.defaultValue.get(); + iws.str(json); + schemas.emplace_back(ArrowJSONHelpers::read(iws)); + } + std::ranges::transform(spec.metadata | + views::filter_string_params_starts_with("input:") | + std::ranges::views::transform( + [](auto const& param) { + return DataSpecUtils::fromMetadataString(param.defaultValue.template get()); + }), + std::back_inserter(matchers), [](auto const& i) { return std::get(i.matcher); }); + + std::vector> fields; + std::ranges::for_each(schemas, + [&fields](auto const& s) { + std::ranges::copy(s->fields(), std::back_inserter(fields)); + }); + + inputSchema = std::make_shared(fields); + expressions = expressions::materializeProjectors(projectors, inputSchema, outputSchema->fields()); + } + + std::shared_ptr makeProjector() const + { + std::shared_ptr p = nullptr; + auto s = gandiva::Projector::Make( + inputSchema, + expressions, + &p); + if (!s.ok()) { + throw o2::framework::runtime_error_f("Failed to create projector: %s", s.ToString().c_str()); + } + return p; + } + + framework::Spawner createMaker() const + { + return { + binding, + labels, + matchers, + expressions, + makeProjector(), + outputSchema, + inputSchema, + origin, + description, + version}; + } +}; + +} // namespace + +AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& /*ctx*/) +{ + return AlgorithmSpec::InitCallback{[](InitContext& ic) { + auto const& requested = ic.services().get().spawnerInputs; + std::vector spawners; + spawners.reserve(requested.size()); + std::ranges::transform(requested, std::back_inserter(spawners), [](auto const& i) { return Spawnable{i}.createMaker(); }); + return [spawners](ProcessingContext& pc) mutable { + auto outputs = pc.outputs(); + std::ranges::for_each(spawners, [&pc, &outputs](auto& spawner) { outputs.adopt(Output{spawner.origin, spawner.description, spawner.version}, spawner.materialize(pc)); }); + }; + }}; +} + +} // namespace o2::framework::readers diff --git a/Framework/Core/include/Framework/AODReaderHelpers.h b/Framework/AnalysisSupport/src/AODReaderHelpers.h similarity index 76% rename from Framework/Core/include/Framework/AODReaderHelpers.h rename to Framework/AnalysisSupport/src/AODReaderHelpers.h index 957a5b1cd25ba..848ef6b696713 100644 --- a/Framework/Core/include/Framework/AODReaderHelpers.h +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.h @@ -12,20 +12,16 @@ #ifndef O2_FRAMEWORK_AODREADERHELPERS_H_ #define O2_FRAMEWORK_AODREADERHELPERS_H_ -#include "Framework/TableBuilder.h" #include "Framework/AlgorithmSpec.h" -#include "Framework/Logger.h" -#include "Framework/RootMessageContext.h" #include namespace o2::framework::readers { - struct AODReaderHelpers { static AlgorithmSpec rootFileReaderCallback(); - static AlgorithmSpec aodSpawnerCallback(std::vector& requested); - static AlgorithmSpec indexBuilderCallback(std::vector& requested); + static AlgorithmSpec aodSpawnerCallback(ConfigContext const& /*ctx*/); + static AlgorithmSpec indexBuilderCallback(ConfigContext const& /*ctx*/); }; } // namespace o2::framework::readers diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index 40d2189ea96d0..b76ffca13977e 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -8,7 +8,7 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Framework/AnalysisContext.h" +#include "Framework/DanglingEdgesContext.h" #include "Framework/ConfigContext.h" #include "Framework/ControlService.h" #include "AODWriterHelpers.h" @@ -21,13 +21,21 @@ #include "Framework/TableConsumer.h" #include "Framework/DataOutputDirector.h" #include "Framework/TableTreeHelpers.h" +#include "Framework/Monitoring.h" +#include "Framework/Signpost.h" +#include +#include #include #include #include #include #include #include +#include +#include + +O2_DECLARE_DYNAMIC_LOG(histogram_registry); namespace o2::framework::writers { @@ -44,6 +52,7 @@ struct InputObjectRoute { struct InputObject { TClass* kind = nullptr; void* obj = nullptr; + std::string container; std::string name; int count = -1; }; @@ -53,13 +62,13 @@ const static std::unordered_map ROOTfileNa AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) { - auto& ac = ctx.services().get(); auto dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); int compressionLevel = 505; if (ctx.options().hasOption("aod-writer-compression")) { compressionLevel = ctx.options().get("aod-writer-compression"); } - return AlgorithmSpec{[dod, outputInputs = ac.outputsInputsAOD, compressionLevel](InitContext& ic) -> std::function { + return AlgorithmSpec{[dod, compressionLevel](InitContext& ic) -> std::function { + auto outputInputs = ic.services().get().outputsInputsAOD; LOGP(debug, "======== getGlobalAODSink::Init =========="); // find out if any table needs to be saved @@ -176,13 +185,12 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) } // get the TableConsumer and corresponding arrow table - auto msg = pc.inputs().get(ref.spec->binding); - if (msg.header == nullptr) { + if (ref.header == nullptr) { LOGP(error, "No header for message {}:{}", ref.spec->binding, DataSpecUtils::describe(*ref.spec)); continue; } - auto s = pc.inputs().get(ref.spec->binding); - auto table = s->asArrowTable(); + + auto table = pc.inputs().get(std::get(ref.spec->matcher))->asArrowTable(); if (!table->Validate().ok()) { LOGP(warning, "The table \"{}\" is not valid and will not be saved!", tableName); continue; @@ -233,13 +241,13 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) }; } -AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) +AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ctx*/) { - auto& ac = ctx.services().get(); - auto tskmap = ac.outTskMap; - auto objmap = ac.outObjHistMap; - - return AlgorithmSpec{[objmap, tskmap](InitContext& ic) -> std::function { + return AlgorithmSpec{[](InitContext& ic) -> std::function { + using namespace monitoring; + auto& dec = ic.services().get(); + auto tskmap = dec.outTskMap; + auto objmap = dec.outObjHistMap; auto& callbacks = ic.services().get(); auto inputObjects = std::make_shared>>(); @@ -270,24 +278,30 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) callbacks.set(endofdatacb); return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { auto mergePart = [&inputObjects, &objmap, &tskmap](DataRef const& ref) { + O2_SIGNPOST_ID_GENERATE(hid, histogram_registry); + O2_SIGNPOST_START(histogram_registry, hid, "mergePart", "Merging histogram"); if (!ref.header) { - LOG(error) << "Header not found"; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "Header not found."); return; } auto datah = o2::header::get(ref.header); if (!datah) { - LOG(error) << "No data header in stack"; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No data header in stack"); return; } if (!ref.payload) { - LOGP(error, "Payload not found for {}/{}/{}", datah->dataOrigin.as(), datah->dataDescription.as(), datah->subSpecification); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "Payload not found for %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); return; } auto objh = o2::header::get(ref.header); if (!objh) { - LOGP(error, "No output object header in stack of {}/{}/{}", datah->dataOrigin.as(), datah->dataDescription.as(), datah->subSpecification); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No output object header in stack of %{public}s/%{public}s/%d.", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); return; } @@ -297,48 +311,75 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) obj.kind = tm.ReadClass(); tm.SetBufferOffset(0); tm.ResetMap(); + O2_SIGNPOST_ID_GENERATE(did, histogram_registry); + O2_SIGNPOST_START(histogram_registry, did, "initialising root", "Starting deserialization of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); if (obj.kind == nullptr) { - LOGP(error, "Cannot read class info from buffer of {}/{}/{}", datah->dataOrigin.as(), datah->dataDescription.as(), datah->subSpecification); + O2_SIGNPOST_END(histogram_registry, did, "initialising root", "Failed to deserialise"); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "Cannot read class info from buffer of %{public}s/%{public}s/%d.", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); return; } + O2_SIGNPOST_END(histogram_registry, did, "initialising root", "Done init."); auto policy = objh->mPolicy; auto sourceType = objh->mSourceType; auto hash = objh->mTaskHash; + O2_SIGNPOST_START(histogram_registry, did, "deserialization", "Starting deserialization of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); obj.obj = tm.ReadObjectAny(obj.kind); auto* named = static_cast(obj.obj); obj.name = named->GetName(); + O2_SIGNPOST_END(histogram_registry, did, "deserialization", "Done deserialization."); + // If we have a folder, we assume the first element of the path + // to be the name of the registry. + bool folderForContainer = false; + if (sourceType == HistogramRegistrySource) { + folderForContainer = objh->createContainer != 0; + obj.container = objh->containerName; + } else { + obj.container = obj.name; + } auto hpos = std::find_if(tskmap.begin(), tskmap.end(), [&](auto&& x) { return x.id == hash; }); if (hpos == tskmap.end()) { - LOG(error) << "No task found for hash " << hash; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No task found for hash %d.", hash); return; } auto taskname = hpos->name; auto opos = std::find_if(objmap.begin(), objmap.end(), [&](auto&& x) { return x.id == hash; }); if (opos == objmap.end()) { - LOG(error) << "No object list found for task " << taskname << " (hash=" << hash << ")"; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No object list found for task %{public}s (hash=%d).", + taskname.c_str(), hash); return; } auto objects = opos->bindings; - if (std::find(objects.begin(), objects.end(), obj.name) == objects.end()) { - LOG(error) << "No object " << obj.name << " in map for task " << taskname; + if (std::find(objects.begin(), objects.end(), obj.container) == objects.end()) { + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No container %{public}s in map for task %{public}s.", + obj.container.c_str(), taskname.c_str()); return; } auto nameHash = runtime_hash(obj.name.c_str()); InputObjectRoute key{obj.name, nameHash, taskname, hash, policy, sourceType}; auto existing = std::find_if(inputObjects->begin(), inputObjects->end(), [&](auto&& x) { return (x.first.uniqueId == nameHash) && (x.first.taskHash == hash); }); // If it's the first one, we just add it to the list. + O2_SIGNPOST_START(histogram_registry, did, "merging", "Starting merging of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); if (existing == inputObjects->end()) { obj.count = objh->mPipelineSize; - inputObjects->push_back(std::make_pair(key, obj)); + inputObjects->emplace_back(key, obj); existing = inputObjects->end() - 1; } else { obj.count = existing->second.count; // Otherwise, we merge it with the existing one. auto merger = existing->second.kind->GetMerge(); if (!merger) { - LOG(error) << "Already one unmergeable object found for " << obj.name; + O2_SIGNPOST_END(histogram_registry, did, "merging", "Unabled to merge"); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "merging", "Already one unmergeable object found for %{public}s", obj.name.c_str()); return; } TList coll; @@ -350,15 +391,22 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) existing->second.count -= 1; if (existing->second.count != 0) { + O2_SIGNPOST_END(histogram_registry, did, "merging", "Done partial merging."); + O2_SIGNPOST_END(histogram_registry, hid, "mergePart", "Pipeline lanes still missing."); return; } + O2_SIGNPOST_END(histogram_registry, did, "merging", "Done merging."); // Write the object here. auto route = existing->first; auto entry = existing->second; auto file = ROOTfileNames.find(route.policy); if (file == ROOTfileNames.end()) { + O2_SIGNPOST_END(histogram_registry, hid, "mergePart", "Not matching any file."); return; } + O2_SIGNPOST_START(histogram_registry, did, "writing", "Starting writing of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); auto filename = file->second; if (f[route.policy] == nullptr) { f[route.policy] = TFile::Open(filename.c_str(), "RECREATE"); @@ -372,47 +420,63 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) currentFile = filename; } - // translate the list-structure created by the registry into a directory structure within the file - std::function writeListToFile; - writeListToFile = [&](TList* list, TDirectory* parentDir) { - TIter next(list); - TObject* object = nullptr; - while ((object = next())) { - if (object->InheritsFrom(TList::Class())) { - writeListToFile(static_cast(object), parentDir->mkdir(object->GetName(), object->GetName(), true)); + // FIXME: handle folders + f[route.policy]->cd("/"); + auto* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); + + // In case we need a folder for the registry, let's create it. + if (folderForContainer) { + auto* histogramRegistryFolder = currentDir->GetDirectory(obj.container.data()); + if (!histogramRegistryFolder) { + histogramRegistryFolder = currentDir->mkdir(obj.container.c_str(), "", kTRUE); + } + currentDir = histogramRegistryFolder; + } + + // The name contains a path... + int objSize = 0; + if (sourceType == HistogramRegistrySource) { + TDirectory* currentFolder = currentDir; + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Toplevel folder is %{public}s.", + currentDir->GetName()); + std::string objName = entry.name; + auto lastSlash = entry.name.rfind('/'); + + if (lastSlash != std::string::npos) { + auto dirname = entry.name.substr(0, lastSlash); + objName = entry.name.substr(lastSlash + 1); + currentFolder = currentDir->GetDirectory(dirname.c_str()); + if (!currentFolder) { + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Creating folder %{public}s", + dirname.c_str()); + currentFolder = currentDir->mkdir(dirname.c_str(), "", kTRUE); } else { - parentDir->WriteObjectAny(object, object->Class(), object->GetName()); - auto* written = list->Remove(object); - delete written; + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Folder %{public}s already there.", + currentFolder->GetName()); } } - }; - - TDirectory* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); - if (route.sourceType == OutputObjSourceType::HistogramRegistrySource) { - auto* outputList = static_cast(entry.obj); - outputList->SetOwner(false); - - // if registry should live in dedicated folder a TNamed object is appended to the list - if (outputList->Last() && outputList->Last()->IsA() == TNamed::Class()) { - delete outputList->Last(); - outputList->RemoveLast(); - currentDir = currentDir->mkdir(outputList->GetName(), outputList->GetName(), true); - } - - writeListToFile(outputList, currentDir); - outputList->SetOwner(); - delete outputList; + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Writing %{public}s of kind %{public}s in %{public}s", + entry.name.c_str(), entry.kind->GetName(), currentDir->GetName()); + objSize = currentFolder->WriteObjectAny(entry.obj, entry.kind, objName.c_str()); + O2_SIGNPOST_END(histogram_registry, did, "writing", "End writing %{public}s", entry.name.c_str()); + delete (TObject*)entry.obj; entry.obj = nullptr; } else { - currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Writing %{public}s of kind %{public}s in %{public}s", + entry.name.c_str(), entry.kind->GetName(), currentDir->GetName()); + objSize = currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + O2_SIGNPOST_END(histogram_registry, did, "writing", "End writing %{public}s", entry.name.c_str()); delete (TObject*)entry.obj; entry.obj = nullptr; } + O2_SIGNPOST_END(histogram_registry, hid, "mergePart", "Done merging object of %d bytes.", objSize); }; - for (int pi = 0; pi < pc.inputs().getNofParts(0); ++pi) { + O2_SIGNPOST_ID_GENERATE(rid, histogram_registry); + O2_SIGNPOST_START(histogram_registry, rid, "processParts", "Start merging %zu parts received together.", pc.inputs().getNofParts(0)); + for (auto pi = 0U; pi < pc.inputs().getNofParts(0); ++pi) { mergePart(pc.inputs().get("x", pi)); } + O2_SIGNPOST_END(histogram_registry, rid, "processParts", "Done histograms in multipart message."); }; }}; } diff --git a/Framework/AnalysisSupport/src/DataInputDirector.cxx b/Framework/AnalysisSupport/src/DataInputDirector.cxx index 590329de146f7..2bc6c5613f065 100644 --- a/Framework/AnalysisSupport/src/DataInputDirector.cxx +++ b/Framework/AnalysisSupport/src/DataInputDirector.cxx @@ -16,8 +16,8 @@ #include "Framework/AnalysisDataModelHelpers.h" #include "Framework/Output.h" #include "Framework/Signpost.h" +#include "Framework/FragmentToBatch.h" #include "Headers/DataHeader.h" -#include "Framework/TableTreeHelpers.h" #include "Monitoring/Tags.h" #include "Monitoring/Metric.h" #include "Monitoring/Monitoring.h" @@ -124,15 +124,22 @@ void DataInputDescriptor::addFileNameHolder(FileNameHolder* fn) mfilenames.emplace_back(fn); } -bool DataInputDescriptor::setFile(int counter) +bool DataInputDescriptor::setFile(int counter, std::string_view origin) { // no files left if (counter >= getNumberInputfiles()) { return false; } + // In case the origin starts with a anything but AOD, we add the origin as the suffix + // of the filename. In the future we might expand this for proper rewriting of the + // filename based on the origin and the original file information. + std::string filename = mfilenames[counter]->fileName; + if (!origin.starts_with("AOD")) { + filename = std::regex_replace(filename, std::regex("[.]root$"), fmt::format("_{}.root", origin)); + } + // open file - auto filename = mfilenames[counter]->fileName; auto rootFS = std::dynamic_pointer_cast(mCurrentFilesystem); if (rootFS.get()) { if (rootFS->GetFile()->GetName() == filename) { @@ -213,11 +220,11 @@ bool DataInputDescriptor::setFile(int counter) return true; } -uint64_t DataInputDescriptor::getTimeFrameNumber(int counter, int numTF) +uint64_t DataInputDescriptor::getTimeFrameNumber(int counter, int numTF, std::string_view origin) { // open file - if (!setFile(counter)) { + if (!setFile(counter, origin)) { return 0ul; } @@ -229,10 +236,10 @@ uint64_t DataInputDescriptor::getTimeFrameNumber(int counter, int numTF) return (mfilenames[counter]->listOfTimeFrameNumbers)[numTF]; } -arrow::dataset::FileSource DataInputDescriptor::getFileFolder(int counter, int numTF) +arrow::dataset::FileSource DataInputDescriptor::getFileFolder(int counter, int numTF, std::string_view origin) { // open file - if (!setFile(counter)) { + if (!setFile(counter, origin)) { return {}; } @@ -246,7 +253,7 @@ arrow::dataset::FileSource DataInputDescriptor::getFileFolder(int counter, int n return {fmt::format("DF_{}", mfilenames[counter]->listOfTimeFrameNumbers[numTF]), mCurrentFilesystem}; } -DataInputDescriptor* DataInputDescriptor::getParentFile(int counter, int numTF, std::string treename) +DataInputDescriptor* DataInputDescriptor::getParentFile(int counter, int numTF, std::string treename, std::string_view origin) { if (!mParentFileMap) { // This file has no parent map @@ -283,7 +290,7 @@ DataInputDescriptor* DataInputDescriptor::getParentFile(int counter, int numTF, mParentFile->mdefaultFilenamesPtr = new std::vector; mParentFile->mdefaultFilenamesPtr->emplace_back(makeFileNameHolder(parentFileName->GetString().Data())); mParentFile->fillInputfiles(); - mParentFile->setFile(0); + mParentFile->setFile(0, origin); return mParentFile; } @@ -427,7 +434,8 @@ struct CalculateDelta { mTarget += (uv_hrtime() - start); } - void deactivate() { + void deactivate() + { active = false; } @@ -440,7 +448,8 @@ struct CalculateDelta { bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh, int counter, int numTF, std::string treename, size_t& totalSizeCompressed, size_t& totalSizeUncompressed) { CalculateDelta t(mIOTime); - auto folder = getFileFolder(counter, numTF); + std::string origin = dh.dataOrigin.as(); + auto folder = getFileFolder(counter, numTF, origin); if (!folder.filesystem()) { t.deactivate(); return false; @@ -473,7 +482,7 @@ bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh if (!format) { t.deactivate(); LOGP(debug, "Could not find tree {}. Trying in parent file.", fullpath.path()); - auto parentFile = getParentFile(counter, numTF, treename); + auto parentFile = getParentFile(counter, numTF, treename, origin); if (parentFile != nullptr) { int parentNumTF = parentFile->findDFNumber(0, folder.path()); if (parentNumTF == -1) { @@ -817,8 +826,9 @@ arrow::dataset::FileSource DataInputDirector::getFileFolder(header::DataHeader d if (!didesc) { didesc = mdefaultDataInputDescriptor; } + std::string origin = dh.dataOrigin.as(); - return didesc->getFileFolder(counter, numTF); + return didesc->getFileFolder(counter, numTF, origin); } int DataInputDirector::getTimeFramesInFile(header::DataHeader dh, int counter) @@ -839,8 +849,9 @@ uint64_t DataInputDirector::getTimeFrameNumber(header::DataHeader dh, int counte if (!didesc) { didesc = mdefaultDataInputDescriptor; } + std::string origin = dh.dataOrigin.as(); - return didesc->getTimeFrameNumber(counter, numTF); + return didesc->getTimeFrameNumber(counter, numTF, origin); } bool DataInputDirector::readTree(DataAllocator& outputs, header::DataHeader dh, int counter, int numTF, size_t& totalSizeCompressed, size_t& totalSizeUncompressed) @@ -858,6 +869,7 @@ bool DataInputDirector::readTree(DataAllocator& outputs, header::DataHeader dh, didesc = mdefaultDataInputDescriptor; treename = aod::datamodel::getTreeName(dh); } + std::string origin = dh.dataOrigin.as(); auto result = didesc->readTree(outputs, dh, counter, numTF, treename, totalSizeCompressed, totalSizeUncompressed); return result; diff --git a/Framework/AnalysisSupport/src/DataInputDirector.h b/Framework/AnalysisSupport/src/DataInputDirector.h index 94bdcf2c9368e..61b477bd8522d 100644 --- a/Framework/AnalysisSupport/src/DataInputDirector.h +++ b/Framework/AnalysisSupport/src/DataInputDirector.h @@ -64,7 +64,7 @@ class DataInputDescriptor void addFileNameHolder(FileNameHolder* fn); int fillInputfiles(); - bool setFile(int counter); + bool setFile(int counter, std::string_view origin); // getters std::string getInputfilesFilename(); @@ -74,9 +74,9 @@ class DataInputDescriptor int getNumberTimeFrames() { return mtotalNumberTimeFrames; } int findDFNumber(int file, std::string dfName); - uint64_t getTimeFrameNumber(int counter, int numTF); - arrow::dataset::FileSource getFileFolder(int counter, int numTF); - DataInputDescriptor* getParentFile(int counter, int numTF, std::string treename); + uint64_t getTimeFrameNumber(int counter, int numTF, std::string_view origin); + arrow::dataset::FileSource getFileFolder(int counter, int numTF, std::string_view origin); + DataInputDescriptor* getParentFile(int counter, int numTF, std::string treename, std::string_view origin); int getTimeFramesInFile(int counter); int getReadTimeFramesInFile(int counter); diff --git a/Framework/AnalysisSupport/src/OnDemandPlugin.cxx b/Framework/AnalysisSupport/src/OnDemandPlugin.cxx new file mode 100644 index 0000000000000..9438f9bf69c96 --- /dev/null +++ b/Framework/AnalysisSupport/src/OnDemandPlugin.cxx @@ -0,0 +1,32 @@ +// 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. +#include "Framework/Plugins.h" +#include "Framework/AlgorithmSpec.h" +#include "AODReaderHelpers.h" + +struct ExtendedTableSpawner : o2::framework::AlgorithmPlugin { + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override + { + return o2::framework::readers::AODReaderHelpers::aodSpawnerCallback(config); + } +}; + +struct IndexTableBuilder : o2::framework::AlgorithmPlugin { + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override + { + return o2::framework::readers::AODReaderHelpers::indexBuilderCallback(config); + } +}; + +DEFINE_DPL_PLUGINS_BEGIN +DEFINE_DPL_PLUGIN_INSTANCE(ExtendedTableSpawner, CustomAlgorithm); +DEFINE_DPL_PLUGIN_INSTANCE(IndexTableBuilder, CustomAlgorithm); +DEFINE_DPL_PLUGINS_END diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index e39e76f01dbdd..5f61a236cbd58 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -27,6 +27,12 @@ O2_DECLARE_DYNAMIC_LOG(analysis_support); +struct ROOTTypeInfo { + EDataType type; + char suffix[3]; + int size; +}; + struct ROOTFileReader : o2::framework::AlgorithmPlugin { o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override { diff --git a/Framework/Core/src/TableTreeHelpers.cxx b/Framework/AnalysisSupport/src/TableTreeHelpers.cxx similarity index 93% rename from Framework/Core/src/TableTreeHelpers.cxx rename to Framework/AnalysisSupport/src/TableTreeHelpers.cxx index 92231cb9ce069..800a31e8ecac3 100644 --- a/Framework/Core/src/TableTreeHelpers.cxx +++ b/Framework/AnalysisSupport/src/TableTreeHelpers.cxx @@ -296,31 +296,4 @@ struct BranchInfo { }; } // namespace -FragmentToBatch::FragmentToBatch(StreamerCreator creator, std::shared_ptr fragment, arrow::MemoryPool* pool) - : mFragment{std::move(fragment)}, - mArrowMemoryPool{pool}, - mCreator{std::move(creator)} -{ -} - -void FragmentToBatch::setLabel(const char* label) -{ - mTableLabel = label; -} - -void FragmentToBatch::fill(std::shared_ptr schema, std::shared_ptr format) -{ - auto options = std::make_shared(); - options->dataset_schema = schema; - auto scanner = format->ScanBatchesAsync(options, mFragment); - auto batch = (*scanner)(); - mRecordBatch = *batch.result(); - // Notice that up to here the buffer was not yet filled. -} - -std::shared_ptr FragmentToBatch::finalize() -{ - return mRecordBatch; -} - } // namespace o2::framework diff --git a/Framework/Core/test/benchmark_TableToTree.cxx b/Framework/AnalysisSupport/test/benchmark_TableToTree.cxx similarity index 100% rename from Framework/Core/test/benchmark_TableToTree.cxx rename to Framework/AnalysisSupport/test/benchmark_TableToTree.cxx diff --git a/Framework/CCDBSupport/CMakeLists.txt b/Framework/CCDBSupport/CMakeLists.txt index e4310ac5e0ec5..ed898fb3114aa 100644 --- a/Framework/CCDBSupport/CMakeLists.txt +++ b/Framework/CCDBSupport/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. # @@ -9,14 +9,21 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. o2_add_library(FrameworkCCDBSupport - SOURCES + SOURCES src/Plugin.cxx + src/CCDBFetcherHelper.cxx src/CCDBHelpers.cxx + src/AnalysisCCDBHelpers.cxx PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src PUBLIC_LINK_LIBRARIES O2::Framework O2::CCDB) -o2_add_test(CCDBHelpers NAME test_Framework_test_CCDBHelpers - SOURCES test/test_CCDBHelpers.cxx - COMPONENT_NAME Framework - LABELS framework - PUBLIC_LINK_LIBRARIES O2::Framework O2::FrameworkCCDBSupport) +add_executable(o2-test-framework-ccdbsupport + test/test_CCDBHelpers.cxx) +target_link_libraries(o2-test-framework-ccdbsupport PRIVATE O2::Framework) +target_link_libraries(o2-test-framework-ccdbsupport PRIVATE O2::FrameworkCCDBSupport) +target_link_libraries(o2-test-framework-ccdbsupport PRIVATE O2::Catch2) + +get_filename_component(outdir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../tests ABSOLUTE) +set_property(TARGET o2-test-framework-ccdbsupport PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) + +add_test(NAME framework:ccdbsupport COMMAND o2-test-framework-ccdbsupport --skip-benchmarks) diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx new file mode 100644 index 0000000000000..ea13d412cd0b8 --- /dev/null +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -0,0 +1,189 @@ +// 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. + +#include "AnalysisCCDBHelpers.h" +#include "CCDBFetcherHelper.h" +#include "Framework/DeviceSpec.h" +#include "Framework/TimingInfo.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataTakingContext.h" +#include "Framework/RawDeviceService.h" +#include "Framework/Output.h" +#include "Framework/Signpost.h" +#include "Framework/DanglingEdgesContext.h" +#include "Framework/ConfigContext.h" +#include "Framework/ConfigContext.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +O2_DECLARE_DYNAMIC_LOG(ccdb); + +namespace o2::framework +{ +// Fill valid routes. Notice that for analysis the timestamps are associated to +// a ATIM table and there might be multiple CCDB objects of the same kind for +// dataframe. +// For this reason rather than matching the Lifetime::Condition, we match the +// origin. +namespace +{ +void fillValidRoutes(CCDBFetcherHelper& helper, std::vector const& outputRoutes, std::unordered_map& bindings) +{ + for (auto& route : outputRoutes) { + auto originMatcher = DataSpecUtils::asConcreteDataMatcher(route.matcher); + if (originMatcher.origin != header::DataOrigin{"ATIM"}) { + continue; + } + auto specStr = DataSpecUtils::describe(route.matcher); + if (bindings.find(specStr) != bindings.end()) { + continue; + } + bindings[specStr] = helper.routes.size(); + helper.routes.push_back(route); + LOGP(info, "The following route needs condition objects {} ", DataSpecUtils::describe(route.matcher)); + for (auto& metadata : route.matcher.metadata) { + if (metadata.type == VariantType::String) { + LOGP(info, "- {}: {}", metadata.name, metadata.defaultValue.asString()); + } + } + } +} +} // namespace + +AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) +{ + return adaptStateful([](ConfigParamRegistry const& options, DeviceSpec const& spec, InitContext& ic) { + auto& dec = ic.services().get(); + std::vector> schemas; + auto schemaMetadata = std::make_shared(); + + for (auto& input : dec.analysisCCDBInputs) { + std::vector> fields; + schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); + schemaMetadata->Append("outputBinding", input.binding); + + for (auto& m : input.metadata) { + // Save the list of input tables + if (m.name.starts_with("input:")) { + auto name = m.name.substr(6); + schemaMetadata->Append("sourceTable", name); + continue; + } + // Ignore the non ccdb: entries + if (!m.name.starts_with("ccdb:")) { + continue; + } + // Create the schema of the output + auto metadata = std::make_shared(); + metadata->Append("url", m.defaultValue.asString()); + auto columnName = m.name.substr(strlen("ccdb:")); + fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, metadata)); + } + schemas.emplace_back(std::make_shared(fields, schemaMetadata)); + } + + std::shared_ptr helper = std::make_shared(); + CCDBFetcherHelper::initialiseHelper(*helper, options); + std::unordered_map bindings; + fillValidRoutes(*helper, spec.outputs, bindings); + + return adaptStateless([schemas, bindings, helper](InputRecord& inputs, DataTakingContext& dtc, DataAllocator& allocator, TimingInfo& timingInfo) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb); + O2_SIGNPOST_START(ccdb, sid, "fetchFromAnalysisCCDB", "Fetching CCDB objects for analysis%" PRIu64, (uint64_t)timingInfo.timeslice); + for (auto& schema : schemas) { + std::vector ops; + auto inputBinding = *schema->metadata()->Get("sourceTable"); + auto inputMatcher = DataSpecUtils::fromString(*schema->metadata()->Get("sourceMatcher")); + auto outRouteDesc = *schema->metadata()->Get("outputRoute"); + std::string outBinding = *schema->metadata()->Get("outputBinding"); + O2_SIGNPOST_EVENT_EMIT_INFO(ccdb, sid, "fetchFromAnalysisCCDB", + "Fetching CCDB objects for %{public}s's columns with timestamps from %{public}s and putting them in route %{public}s", + outBinding.c_str(), inputBinding.c_str(), outRouteDesc.c_str()); + auto table = inputs.get(inputMatcher)->asArrowTable(); + // FIXME: make the fTimestamp column configurable. + auto timestampColumn = table->GetColumnByName("fTimestamp"); + O2_SIGNPOST_EVENT_EMIT_INFO(ccdb, sid, "fetchFromAnalysisCCDB", + "There are %zu bindings available", bindings.size()); + for (auto& binding : bindings) { + O2_SIGNPOST_EVENT_EMIT_INFO(ccdb, sid, "fetchFromAnalysisCCDB", + "* %{public}s: %d", + binding.first.c_str(), binding.second); + } + int outputRouteIndex = bindings.at(outRouteDesc); + auto& spec = helper->routes[outputRouteIndex].matcher; + std::vector> builders; + for (auto const& _ : schema->fields()) { + builders.emplace_back(std::make_shared()); + } + + for (auto ci = 0; ci < timestampColumn->num_chunks(); ++ci) { + std::shared_ptr chunk = timestampColumn->chunk(ci); + auto const* timestamps = chunk->data()->GetValuesSafe(1); + + for (int64_t ri = 0; ri < chunk->data()->length; ri++) { + ops.clear(); + int64_t timestamp = timestamps[ri]; + for (auto& field : schema->fields()) { + auto url = *field->metadata()->Get("url"); + // Time to actually populate the blob + ops.push_back({ + .spec = spec, + .url = url, + .timestamp = timestamp, + .runNumber = 1, + .runDependent = 0, + .queryRate = 0, + }); + } + auto responses = CCDBFetcherHelper::populateCacheWith(helper, ops, timingInfo, dtc, allocator); + O2_SIGNPOST_START(ccdb, sid, "handlingResponses", + "Got %zu responses from server.", + responses.size()); + if (builders.size() != responses.size()) { + LOGP(fatal, "Not enough responses (expected {}, found {})", builders.size(), responses.size()); + } + arrow::Status result; + for (size_t bi = 0; bi < responses.size(); bi++) { + auto& builder = builders[bi]; + auto& response = responses[bi]; + char const* address = reinterpret_cast(response.id.value); + result &= builder->Append(std::string_view(address, response.size)); + } + if (!result.ok()) { + LOGP(fatal, "Error adding results from CCDB"); + } + O2_SIGNPOST_END(ccdb, sid, "handlingResponses", "Done processing responses"); + } + } + arrow::ArrayVector arrays; + for (auto& builder : builders) { + arrays.push_back(*builder->Finish()); + } + auto outTable = arrow::Table::Make(schema, arrays); + auto concrete = DataSpecUtils::asConcreteDataMatcher(spec); + allocator.adopt(Output{concrete.origin, concrete.description, concrete.subSpec}, outTable); + } + + O2_SIGNPOST_END(ccdb, sid, "fetchFromAnalysisCCDB", "Fetching CCDB objects"); + }); + }); +} + +} // namespace o2::framework diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h similarity index 56% rename from GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h rename to Framework/CCDBSupport/src/AnalysisCCDBHelpers.h index 7d9d607b9b88d..3be2138bd2b5c 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h @@ -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. // @@ -8,18 +8,18 @@ // In applying this license CERN does not 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_ANALYSISCCDBHELPERS_H_ +#define O2_FRAMEWORK_ANALYSISCCDBHELPERS_H_ -/// \file GPUTPCDefinitions.h -/// \author Sergey Gorbunov, David Rohr +#include "Framework/AlgorithmSpec.h" -#ifndef GPUTPCDEFINITIONS_H -#define GPUTPCDEFINITIONS_H +namespace o2::framework +{ -#include "AliHLTDataTypes.h" +struct AnalysisCCDBHelpers { + static AlgorithmSpec fetchFromCCDB(ConfigContext const&); +}; -namespace GPUTPCDefinitions -{ -extern const AliHLTComponentDataType fgkTrackletsDataType; -} +} // namespace o2::framework -#endif // GPUTPCDEFINITIONS_H +#endif // O2_FRAMEWORK_ANALYSISCCDBHELPERS_H_ diff --git a/Framework/CCDBSupport/src/CCDBFetcherHelper.cxx b/Framework/CCDBSupport/src/CCDBFetcherHelper.cxx new file mode 100644 index 0000000000000..151703105a1dd --- /dev/null +++ b/Framework/CCDBSupport/src/CCDBFetcherHelper.cxx @@ -0,0 +1,299 @@ +// 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. +#include "CCDBFetcherHelper.h" +#include "Framework/DataTakingContext.h" +#include "Framework/Signpost.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/ConfigParamRegistry.h" +#include +#include + +O2_DECLARE_DYNAMIC_LOG(ccdb); + +namespace o2::framework +{ + +o2::ccdb::CcdbApi& CCDBFetcherHelper::getAPI(const std::string& path) +{ + // find the first = sign in the string. If present drop everything after it + // and between it and the previous /. + auto pos = path.find('='); + if (pos == std::string::npos) { + auto entry = remappings.find(path); + return apis[entry == remappings.end() ? "" : entry->second]; + } + auto pos2 = path.rfind('/', pos); + if (pos2 == std::string::npos || pos2 == pos - 1 || pos2 == 0) { + throw runtime_error_f("Malformed path %s", path.c_str()); + } + auto entry = remappings.find(path.substr(0, pos2)); + return apis[entry == remappings.end() ? "" : entry->second]; +} + +namespace +{ +bool isOnlineRun(DataTakingContext const& dtc) +{ + return dtc.deploymentMode == DeploymentMode::OnlineAUX || dtc.deploymentMode == DeploymentMode::OnlineDDS || dtc.deploymentMode == DeploymentMode::OnlineECS; +} +} // namespace + +void CCDBFetcherHelper::initialiseHelper(CCDBFetcherHelper& helper, ConfigParamRegistry const& options) +{ + auto defHost = options.get("condition-backend"); + auto checkRate = options.get("condition-tf-per-query"); + auto checkMult = options.get("condition-tf-per-query-multiplier"); + helper.useTFSlice = options.get("condition-use-slice-for-prescaling"); + helper.timeToleranceMS = options.get("condition-time-tolerance"); + helper.queryPeriodGlo = checkRate > 0 ? checkRate : std::numeric_limits::max(); + helper.queryPeriodFactor = checkMult == 0 ? 1 : checkMult; + std::string extraCond{}; + if (helper.useTFSlice) { + extraCond = ". Use TFSlice"; + if (helper.useTFSlice > 0) { + extraCond += fmt::format(" + max TFcounter jump <= {}", helper.useTFSlice); + } + } + LOGP(info, "CCDB Backend at: {}, validity check for every {} TF{}{}", defHost, helper.queryPeriodGlo, + helper.queryPeriodFactor == 1 ? std::string{} : (helper.queryPeriodFactor > 0 ? fmt::format(", (query for high-rate objects downscaled by {})", helper.queryPeriodFactor) : fmt::format(", (query downscaled as TFcounter%{})", -helper.queryPeriodFactor)), + extraCond); + LOGP(info, "Hook to enable signposts for CCDB messages at {}", (void*)&private_o2_log_ccdb->stacktrace); + auto remapString = options.get("condition-remap"); + ParserResult result = parseRemappings(remapString.c_str()); + if (!result.error.empty()) { + throw runtime_error_f("Error while parsing remapping string %s", result.error.c_str()); + } + helper.remappings = result.remappings; + helper.apis[""].init(defHost); // default backend + LOGP(info, "Initialised default CCDB host {}", defHost); + // + for (auto& entry : helper.remappings) { // init api instances for every host seen in the remapping + if (helper.apis.find(entry.second) == helper.apis.end()) { + helper.apis[entry.second].init(entry.second); + LOGP(info, "Initialised custom CCDB host {}", entry.second); + } + LOGP(info, "{} is remapped to {}", entry.first, entry.second); + } + helper.createdNotBefore = std::to_string(options.get("condition-not-before")); + helper.createdNotAfter = std::to_string(options.get("condition-not-after")); +} + +CCDBFetcherHelper::ParserResult CCDBFetcherHelper::parseRemappings(char const* str) +{ + std::unordered_map remappings; + std::string currentUrl = ""; + + enum ParsingStates { + IN_BEGIN, + IN_BEGIN_URL, + IN_BEGIN_TARGET, + IN_END_TARGET, + IN_END_URL + }; + ParsingStates state = IN_BEGIN; + + while (true) { + switch (state) { + case IN_BEGIN: { + if (*str == 0) { + return {remappings, ""}; + } + state = IN_BEGIN_URL; + } + case IN_BEGIN_URL: { + if ((strncmp("http://", str, 7) != 0) && (strncmp("https://", str, 8) != 0 && (strncmp("file://", str, 7) != 0))) { + return {remappings, "URL should start with either http:// or https:// or file://"}; + } + state = IN_END_URL; + } break; + case IN_END_URL: { + char const* c = strchr(str, '='); + if (c == nullptr) { + return {remappings, "Expecting at least one target path, missing `='?"}; + } + if ((c - str) == 0) { + return {remappings, "Empty url"}; + } + currentUrl = std::string_view(str, c - str); + state = IN_BEGIN_TARGET; + str = c + 1; + } break; + case IN_BEGIN_TARGET: { + if (*str == 0) { + return {remappings, "Empty target"}; + } + state = IN_END_TARGET; + } break; + case IN_END_TARGET: { + char const* c = strpbrk(str, ",;"); + if (c == nullptr) { + if (remappings.count(str)) { + return {remappings, fmt::format("Path {} requested more than once.", str)}; + } + remappings[std::string(str)] = currentUrl; + return {remappings, ""}; + } + if ((c - str) == 0) { + return {remappings, "Empty target"}; + } + auto key = std::string(str, c - str); + if (remappings.count(str)) { + return {remappings, fmt::format("Path {} requested more than once.", key)}; + } + remappings[key] = currentUrl; + if (*c == ';') { + state = IN_BEGIN_URL; + } else { + state = IN_BEGIN_TARGET; + } + str = c + 1; + } break; + } + } +} + +auto CCDBFetcherHelper::populateCacheWith(std::shared_ptr const& helper, + std::vector const& ops, + TimingInfo& timingInfo, + DataTakingContext& dtc, + DataAllocator& allocator) -> std::vector +{ + int objCnt = -1; + // We use the timeslice, so that we hook into the same interval as the rest of the + // callback. + static bool isOnline = isOnlineRun(dtc); + + auto sid = _o2_signpost_id_t{(int64_t)timingInfo.timeslice}; + O2_SIGNPOST_START(ccdb, sid, "populateCacheWith", "Starting to populate cache with CCDB objects"); + std::vector responses; + for (auto& op : ops) { + int64_t timestampToUse = op.timestamp; + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "Fetching object for route %{public}s", DataSpecUtils::describe(op.spec).data()); + objCnt++; + auto concrete = DataSpecUtils::asConcreteDataMatcher(op.spec); + Output output{concrete.origin, concrete.description, concrete.subSpec}; + auto&& v = allocator.makeVector(output); + std::map metadata; + std::map headers; + std::string path = op.url; + std::string etag = ""; + int chRate = helper->queryPeriodGlo; + bool checkValidity = false; + if (op.runDependent > 0) { + if (op.runDependent == 1) { + metadata["runNumber"] = std::format("{}", op.runNumber); + } else if (op.runDependent == 2) { + timestampToUse = op.runNumber; + } else { + LOGP(fatal, "Undefined ccdb-run-dependent option {} for spec {}/{}/{}", op.runDependent, + concrete.origin.as(), concrete.description.as(), int(concrete.subSpec)); + } + } + for (auto m : op.metadata) { + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "Adding metadata %{public}s: %{public}s to the request", m.key.data(), m.value.data()); + metadata[m.key] = m.value; + } + if (op.queryRate != 0) { + chRate = op.queryRate * helper->queryPeriodFactor; + } + + const auto url2uuid = helper->mapURL2UUID.find(path); + if (url2uuid != helper->mapURL2UUID.end()) { + etag = url2uuid->second.etag; + // We check validity every chRate timeslices or if the cache is expired + uint64_t validUntil = url2uuid->second.cacheValidUntil; + // When the cache was populated. If the cache was populated after the timestamp, we need to check validity. + uint64_t cachePopulatedAt = url2uuid->second.cachePopulatedAt; + // If timestamp is before the time the element was cached or after the claimed validity, we need to check validity, again + // when online. + bool cacheExpired = (validUntil <= timestampToUse) || (op.timestamp < cachePopulatedAt); + if (isOnline || cacheExpired) { + if (!helper->useTFSlice) { + checkValidity = chRate > 0 ? (std::abs(int(timingInfo.tfCounter - url2uuid->second.lastCheckedTF)) >= chRate) : (timingInfo.tfCounter % -chRate) == 0; + } else { + checkValidity = chRate > 0 ? (std::abs(int(timingInfo.timeslice - url2uuid->second.lastCheckedSlice)) >= chRate) : (timingInfo.timeslice % -chRate) == 0; + if (!checkValidity && helper->useTFSlice > std::abs(chRate)) { // make sure the interval is tolerated unless the check rate itself is too large + checkValidity = std::abs(int(timingInfo.tfCounter) - url2uuid->second.lastCheckedTF) > helper->useTFSlice; + } + } + } + } else { + checkValidity = true; // never skip check if the cache is empty + } + + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "checkValidity is %{public}s for tf%{public}s %zu of %{public}s", checkValidity ? "true" : "false", helper->useTFSlice ? "ID" : "Slice", helper->useTFSlice ? timingInfo.timeslice : timingInfo.tfCounter, path.data()); + + const auto& api = helper->getAPI(path); + if (checkValidity && (!api.isSnapshotMode() || etag.empty())) { // in the snapshot mode the object needs to be fetched only once + LOGP(detail, "Loading {} for timestamp {}", path, timestampToUse); + api.loadFileToMemory(v, path, metadata, timestampToUse, &headers, etag, helper->createdNotAfter, helper->createdNotBefore); + if ((headers.count("Error") != 0) || (etag.empty() && v.empty())) { + LOGP(fatal, "Unable to find CCDB object {}/{}", path, timestampToUse); + // FIXME: I should send a dummy message. + continue; + } + // printing in case we find a default entry + if (headers.find("default") != headers.end()) { + LOGP(detail, "******** Default entry used for {} ********", path); + } + helper->mapURL2UUID[path].lastCheckedTF = timingInfo.tfCounter; + helper->mapURL2UUID[path].lastCheckedSlice = timingInfo.timeslice; + if (etag.empty()) { + helper->mapURL2UUID[path].etag = headers["ETag"]; // update uuid + helper->mapURL2UUID[path].cachePopulatedAt = timestampToUse; + helper->mapURL2UUID[path].cacheMiss++; + helper->mapURL2UUID[path].size = v.size(); + helper->mapURL2UUID[path].minSize = std::min(v.size(), helper->mapURL2UUID[path].minSize); + helper->mapURL2UUID[path].maxSize = std::max(v.size(), helper->mapURL2UUID[path].maxSize); + auto size = v.size(); + api.appendFlatHeader(v, headers); + auto cacheId = allocator.adoptContainer(output, std::move(v), DataAllocator::CacheStrategy::Always, header::gSerializationMethodCCDB); + helper->mapURL2DPLCache[path] = cacheId; + responses.emplace_back(Response{.id = cacheId, .size = size, .request = nullptr}); + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "Caching %{public}s for %{public}s (DPL id %" PRIu64 ", size %zu)", path.data(), headers["ETag"].data(), cacheId.value, size); + continue; + } + if (v.size()) { // but should be overridden by fresh object + // somewhere here pruneFromCache should be called + helper->mapURL2UUID[path].etag = headers["ETag"]; // update uuid + helper->mapURL2UUID[path].cachePopulatedAt = timestampToUse; + helper->mapURL2UUID[path].cacheValidUntil = headers["Cache-Valid-Until"].empty() ? 0 : std::stoul(headers["Cache-Valid-Until"]); + helper->mapURL2UUID[path].cacheMiss++; + helper->mapURL2UUID[path].size = v.size(); + helper->mapURL2UUID[path].minSize = std::min(v.size(), helper->mapURL2UUID[path].minSize); + helper->mapURL2UUID[path].maxSize = std::max(v.size(), helper->mapURL2UUID[path].maxSize); + auto size = v.size(); + api.appendFlatHeader(v, headers); + auto cacheId = allocator.adoptContainer(output, std::move(v), DataAllocator::CacheStrategy::Always, header::gSerializationMethodCCDB); + helper->mapURL2DPLCache[path] = cacheId; + responses.emplace_back(Response{.id = cacheId, .size = size, .request = nullptr}); + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "Caching %{public}s for %{public}s (DPL id %" PRIu64 ")", path.data(), headers["ETag"].data(), cacheId.value); + // one could modify the adoptContainer to take optional old cacheID to clean: + // mapURL2DPLCache[URL] = ctx.outputs().adoptContainer(output, std::move(outputBuffer), DataAllocator::CacheStrategy::Always, mapURL2DPLCache[URL]); + continue; + } else { + // Only once the etag is actually used, we get the information on how long the object is valid + helper->mapURL2UUID[path].cacheValidUntil = headers["Cache-Valid-Until"].empty() ? 0 : std::stoul(headers["Cache-Valid-Until"]); + } + } + // cached object is fine + auto cacheId = helper->mapURL2DPLCache[path]; + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "Reusing %{public}s for %{public}s (DPL id %" PRIu64 ")", path.data(), headers["ETag"].data(), cacheId.value); + helper->mapURL2UUID[path].cacheHit++; + responses.emplace_back(Response{.id = cacheId, .size = helper->mapURL2UUID[path].size, .request = nullptr}); + allocator.adoptFromCache(output, cacheId, header::gSerializationMethodCCDB); + // the outputBuffer was not used, can we destroy it? + } + O2_SIGNPOST_END(ccdb, sid, "populateCacheWith", "Finished populating cache with CCDB objects"); + return responses; +}; + +} // namespace o2::framework diff --git a/Framework/CCDBSupport/src/CCDBFetcherHelper.h b/Framework/CCDBSupport/src/CCDBFetcherHelper.h new file mode 100644 index 0000000000000..1778712f45002 --- /dev/null +++ b/Framework/CCDBSupport/src/CCDBFetcherHelper.h @@ -0,0 +1,111 @@ +// 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. +#ifndef O2_FRAMEWORK_CCDBFETCHERHELPER_H_ +#define O2_FRAMEWORK_CCDBFETCHERHELPER_H_ + +#include "Framework/OutputRoute.h" +#include "Framework/DataAllocator.h" +#include "CCDB/CcdbApi.h" +#include +#include + +namespace o2::framework +{ + +struct DataTakingContext; + +struct CCDBFetcherHelper { + struct CCDBCacheInfo { + std::string etag; + size_t cacheValidUntil = 0; + size_t cachePopulatedAt = 0; + size_t cacheMiss = 0; + size_t cacheHit = 0; + size_t size = 0L; + size_t minSize = -1ULL; + size_t maxSize = 0; + int lastCheckedTF = 0; + int lastCheckedSlice = 0; + }; + + struct RemapMatcher { + std::string path; + }; + + struct RemapTarget { + std::string url; + }; + + struct ParserResult { + std::unordered_map remappings; + std::string error; + }; + + struct MetadataEntry { + std::string key; + std::string value; + }; + + // A fetch operation. + struct FetchOp { + // Where to put the blob + OutputSpec& spec; + // The url to fetch + std::string url = ""; + // The timestamp to use + int64_t timestamp = 0; + // The run to use + int runNumber = 0; + // Wether or not the thing is run dependent + int runDependent = 0; + // Actual metadata + std::vector metadata = {}; + // Query rate + int queryRate = 0; + }; + + // Where the data has been fetched + struct Response { + // CacheId / Pointer to the actual data + DataAllocator::CacheId id; + // The size of the buffer + size_t size = 0; + // Where to actually + FetchOp* request = nullptr; + }; + + static ParserResult parseRemappings(char const*); + + std::unordered_map mapURL2UUID; + std::unordered_map mapURL2DPLCache; + std::string createdNotBefore = "0"; + std::string createdNotAfter = "3385078236000"; + std::unordered_map apis; + std::vector routes; + std::unordered_map remappings; + uint32_t lastCheckedTFCounterOrbReset = 0; // last checkecked TFcounter for bulk check + int queryPeriodGlo = 1; + int queryPeriodFactor = 1; + int64_t timeToleranceMS = 5000; + int useTFSlice = 0; // if non-zero, use TFslice instead of TFcounter for the validity check. If > requested checking rate, add additional check on |lastTFchecked - TCcounter|<=useTFSlice + + o2::ccdb::CcdbApi& getAPI(const std::string& path); + static void initialiseHelper(CCDBFetcherHelper& helper, ConfigParamRegistry const& options); + static auto populateCacheWith(std::shared_ptr const& helper, + std::vector const& ops, + TimingInfo& timingInfo, + DataTakingContext& dtc, + DataAllocator& allocator) -> std::vector; +}; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_CCDBFETCHERHELPER_H_ diff --git a/Framework/CCDBSupport/src/CCDBHelpers.cxx b/Framework/CCDBSupport/src/CCDBHelpers.cxx index 0db4cbb5ac71d..80a79796f8c1b 100644 --- a/Framework/CCDBSupport/src/CCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/CCDBHelpers.cxx @@ -20,16 +20,15 @@ #include "CCDB/CcdbApi.h" #include "CommonConstants/LHCConstants.h" #include "Framework/Signpost.h" -#include #include #include -#include O2_DECLARE_DYNAMIC_LOG(ccdb); namespace o2::framework { +namespace { struct CCDBFetcherHelper { struct CCDBCacheInfo { std::string etag; @@ -40,6 +39,7 @@ struct CCDBFetcherHelper { size_t minSize = -1ULL; size_t maxSize = 0; int lastCheckedTF = 0; + int lastCheckedSlice = 0; }; struct RemapMatcher { @@ -61,6 +61,7 @@ struct CCDBFetcherHelper { int queryPeriodGlo = 1; int queryPeriodFactor = 1; int64_t timeToleranceMS = 5000; + int useTFSlice = 0; // if non-zero, use TFslice instead of TFcounter for the validity check. If > requested checking rate, add additional check on |lastTFchecked - TCcounter|<=useTFSlice o2::ccdb::CcdbApi& getAPI(const std::string& path) { @@ -79,6 +80,7 @@ struct CCDBFetcherHelper { return apis[entry == remappings.end() ? "" : entry->second]; } }; +} bool isPrefix(std::string_view prefix, std::string_view full) { @@ -159,6 +161,65 @@ CCDBHelpers::ParserResult CCDBHelpers::parseRemappings(char const* str) } } +void initialiseHelper(CCDBFetcherHelper& helper, ConfigParamRegistry const& options, std::vector const& outputRoutes) +{ + std::unordered_map accountedSpecs; + auto defHost = options.get("condition-backend"); + auto checkRate = options.get("condition-tf-per-query"); + auto checkMult = options.get("condition-tf-per-query-multiplier"); + helper.useTFSlice = options.get("condition-use-slice-for-prescaling"); + helper.timeToleranceMS = options.get("condition-time-tolerance"); + helper.queryPeriodGlo = checkRate > 0 ? checkRate : std::numeric_limits::max(); + helper.queryPeriodFactor = checkMult == 0 ? 1 : checkMult; + std::string extraCond{}; + if (helper.useTFSlice) { + extraCond = ". Use TFSlice"; + if (helper.useTFSlice > 0) { + extraCond += fmt::format(" + max TFcounter jump <= {}", helper.useTFSlice); + } + } + LOGP(info, "CCDB Backend at: {}, validity check for every {} TF{}{}", defHost, helper.queryPeriodGlo, + helper.queryPeriodFactor == 1 ? std::string{} : (helper.queryPeriodFactor > 0 ? fmt::format(", (query for high-rate objects downscaled by {})", helper.queryPeriodFactor) : fmt::format(", (query downscaled as TFcounter%{})", -helper.queryPeriodFactor)), + extraCond); + LOGP(info, "Hook to enable signposts for CCDB messages at {}", (void*)&private_o2_log_ccdb->stacktrace); + auto remapString = options.get("condition-remap"); + CCDBHelpers::ParserResult result = CCDBHelpers::parseRemappings(remapString.c_str()); + if (!result.error.empty()) { + throw runtime_error_f("Error while parsing remapping string %s", result.error.c_str()); + } + helper.remappings = result.remappings; + helper.apis[""].init(defHost); // default backend + LOGP(info, "Initialised default CCDB host {}", defHost); + // + for (auto& entry : helper.remappings) { // init api instances for every host seen in the remapping + if (helper.apis.find(entry.second) == helper.apis.end()) { + helper.apis[entry.second].init(entry.second); + LOGP(info, "Initialised custom CCDB host {}", entry.second); + } + LOGP(info, "{} is remapped to {}", entry.first, entry.second); + } + helper.createdNotBefore = std::to_string(options.get("condition-not-before")); + helper.createdNotAfter = std::to_string(options.get("condition-not-after")); + + for (auto& route : outputRoutes) { + if (route.matcher.lifetime != Lifetime::Condition) { + continue; + } + auto specStr = DataSpecUtils::describe(route.matcher); + if (accountedSpecs.find(specStr) != accountedSpecs.end()) { + continue; + } + accountedSpecs[specStr] = true; + helper.routes.push_back(route); + LOGP(info, "The following route is a condition {}", DataSpecUtils::describe(route.matcher)); + for (auto& metadata : route.matcher.metadata) { + if (metadata.type == VariantType::String) { + LOGP(info, "- {}: {}", metadata.name, metadata.defaultValue.asString()); + } + } + } +} + auto getOrbitResetTime(o2::pmr::vector const& v) -> Long64_t { Int_t previousErrorLevel = gErrorIgnoreLevel; @@ -227,7 +288,7 @@ auto populateCacheWith(std::shared_ptr const& helper, O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "Adding metadata %{public}s: %{public}s to the request", key.data(), value.data()); metadata[key] = value; } else if (meta.name == "ccdb-query-rate") { - chRate = meta.defaultValue.get() * helper->queryPeriodFactor; + chRate = std::max(1, meta.defaultValue.get()) * helper->queryPeriodFactor; } } const auto url2uuid = helper->mapURL2UUID.find(path); @@ -240,12 +301,21 @@ auto populateCacheWith(std::shared_ptr const& helper, // If timestamp is before the time the element was cached or after the claimed validity, we need to check validity, again // when online. bool cacheExpired = (validUntil <= timestampToUse) || (timestamp < cachePopulatedAt); - checkValidity = (std::abs(int(timingInfo.tfCounter - url2uuid->second.lastCheckedTF)) >= chRate) && (isOnline || cacheExpired); + if (isOnline || cacheExpired) { + if (!helper->useTFSlice) { + checkValidity = chRate > 0 ? (std::abs(int(timingInfo.tfCounter - url2uuid->second.lastCheckedTF)) >= chRate) : (timingInfo.tfCounter % -chRate) == 0; + } else { + checkValidity = chRate > 0 ? (std::abs(int(timingInfo.timeslice - url2uuid->second.lastCheckedSlice)) >= chRate) : (timingInfo.timeslice % -chRate) == 0; + if (!checkValidity && helper->useTFSlice > std::abs(chRate)) { // make sure the interval is tolerated unless the check rate itself is too large + checkValidity = std::abs(int(timingInfo.tfCounter) - url2uuid->second.lastCheckedTF) > helper->useTFSlice; + } + } + } } else { checkValidity = true; // never skip check if the cache is empty } - O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "checkValidity is %{public}s for tfID %d of %{public}s", checkValidity ? "true" : "false", timingInfo.tfCounter, path.data()); + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "populateCacheWith", "checkValidity is %{public}s for tf%{public}s %zu of %{public}s", checkValidity ? "true" : "false", helper->useTFSlice ? "ID" : "Slice", helper->useTFSlice ? timingInfo.timeslice : timingInfo.tfCounter, path.data()); const auto& api = helper->getAPI(path); if (checkValidity && (!api.isSnapshotMode() || etag.empty())) { // in the snapshot mode the object needs to be fetched only once @@ -261,6 +331,7 @@ auto populateCacheWith(std::shared_ptr const& helper, LOGP(detail, "******** Default entry used for {} ********", path); } helper->mapURL2UUID[path].lastCheckedTF = timingInfo.tfCounter; + helper->mapURL2UUID[path].lastCheckedSlice = timingInfo.timeslice; if (etag.empty()) { helper->mapURL2UUID[path].etag = headers["ETag"]; // update uuid helper->mapURL2UUID[path].cachePopulatedAt = timestampToUse; @@ -307,51 +378,7 @@ AlgorithmSpec CCDBHelpers::fetchFromCCDB() { return adaptStateful([](CallbackService& callbacks, ConfigParamRegistry const& options, DeviceSpec const& spec) { std::shared_ptr helper = std::make_shared(); - std::unordered_map accountedSpecs; - auto defHost = options.get("condition-backend"); - auto checkRate = options.get("condition-tf-per-query"); - auto checkMult = options.get("condition-tf-per-query-multiplier"); - helper->timeToleranceMS = options.get("condition-time-tolerance"); - helper->queryPeriodGlo = checkRate > 0 ? checkRate : std::numeric_limits::max(); - helper->queryPeriodFactor = checkMult > 0 ? checkMult : 1; - LOGP(info, "CCDB Backend at: {}, validity check for every {} TF{}", defHost, helper->queryPeriodGlo, helper->queryPeriodFactor == 1 ? std::string{} : fmt::format(", (query for high-rate objects downscaled by {})", helper->queryPeriodFactor)); - LOGP(info, "Hook to enable signposts for CCDB messages at {}", (void*)&private_o2_log_ccdb->stacktrace); - auto remapString = options.get("condition-remap"); - ParserResult result = CCDBHelpers::parseRemappings(remapString.c_str()); - if (!result.error.empty()) { - throw runtime_error_f("Error while parsing remapping string %s", result.error.c_str()); - } - helper->remappings = result.remappings; - helper->apis[""].init(defHost); // default backend - LOGP(info, "Initialised default CCDB host {}", defHost); - // - for (auto& entry : helper->remappings) { // init api instances for every host seen in the remapping - if (helper->apis.find(entry.second) == helper->apis.end()) { - helper->apis[entry.second].init(entry.second); - LOGP(info, "Initialised custom CCDB host {}", entry.second); - } - LOGP(info, "{} is remapped to {}", entry.first, entry.second); - } - helper->createdNotBefore = std::to_string(options.get("condition-not-before")); - helper->createdNotAfter = std::to_string(options.get("condition-not-after")); - - for (auto &route : spec.outputs) { - if (route.matcher.lifetime != Lifetime::Condition) { - continue; - } - auto specStr = DataSpecUtils::describe(route.matcher); - if (accountedSpecs.find(specStr) != accountedSpecs.end()) { - continue; - } - accountedSpecs[specStr] = true; - helper->routes.push_back(route); - LOGP(info, "The following route is a condition {}", DataSpecUtils::describe(route.matcher)); - for (auto& metadata : route.matcher.metadata) { - if (metadata.type == VariantType::String) { - LOGP(info, "- {}: {}", metadata.name, metadata.defaultValue.asString()); - } - } - } + initialiseHelper(*helper, options, spec.outputs); /// Add a callback on stop which dumps the statistics for the caching per /// path callbacks.set([helper]() { @@ -377,21 +404,22 @@ AlgorithmSpec CCDBHelpers::fetchFromCCDB() std::map metadata; std::map headers; std::string etag; - bool checkValidity = std::abs(int(timingInfo.tfCounter - helper->lastCheckedTFCounterOrbReset)) >= helper->queryPeriodGlo; + int32_t counter = helper->useTFSlice ? timingInfo.timeslice : timingInfo.tfCounter; + bool checkValidity = std::abs(int(counter - helper->lastCheckedTFCounterOrbReset)) >= helper->queryPeriodGlo; const auto url2uuid = helper->mapURL2UUID.find(path); if (url2uuid != helper->mapURL2UUID.end()) { etag = url2uuid->second.etag; } else { checkValidity = true; // never skip check if the cache is empty } - O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "fetchFromCCDB", "checkValidity is %{public}s for tfID %d of %{public}s", - checkValidity ? "true" : "false", timingInfo.tfCounter, path.data()); + O2_SIGNPOST_EVENT_EMIT(ccdb, sid, "fetchFromCCDB", "checkValidity is %{public}s for tf%{public}s %d of %{public}s", + checkValidity ? "true" : "false", helper->useTFSlice ? "ID" : "Slice", counter, path.data()); Output output{"CTP", "OrbitReset", 0}; Long64_t newOrbitResetTime = orbitResetTime; auto&& v = allocator.makeVector(output); const auto& api = helper->getAPI(path); if (checkValidity && (!api.isSnapshotMode() || etag.empty())) { // in the snapshot mode the object needs to be fetched only once - helper->lastCheckedTFCounterOrbReset = timingInfo.tfCounter; + helper->lastCheckedTFCounterOrbReset = counter; api.loadFileToMemory(v, path, metadata, timingInfo.creation, &headers, etag, helper->createdNotAfter, helper->createdNotBefore); if ((headers.count("Error") != 0) || (etag.empty() && v.empty())) { LOGP(fatal, "Unable to find CCDB object {}/{}", path, timingInfo.creation); diff --git a/Framework/CCDBSupport/src/Plugin.cxx b/Framework/CCDBSupport/src/Plugin.cxx index 18aabc07ae4a4..d9083f97a023e 100644 --- a/Framework/CCDBSupport/src/Plugin.cxx +++ b/Framework/CCDBSupport/src/Plugin.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/Plugins.h" #include "Framework/AlgorithmSpec.h" +#include "AnalysisCCDBHelpers.h" #include "CCDBHelpers.h" struct CCDBFetcherPlugin : o2::framework::AlgorithmPlugin { @@ -19,6 +20,14 @@ struct CCDBFetcherPlugin : o2::framework::AlgorithmPlugin { } }; +struct AnalysisCCDBFetcherPlugin : o2::framework::AlgorithmPlugin { + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& ctx) final + { + return o2::framework::AnalysisCCDBHelpers::fetchFromCCDB(ctx); + } +}; + DEFINE_DPL_PLUGINS_BEGIN DEFINE_DPL_PLUGIN_INSTANCE(CCDBFetcherPlugin, CustomAlgorithm); +DEFINE_DPL_PLUGIN_INSTANCE(AnalysisCCDBFetcherPlugin, CustomAlgorithm); DEFINE_DPL_PLUGINS_END diff --git a/Framework/CCDBSupport/test/test_CCDBHelpers.cxx b/Framework/CCDBSupport/test/test_CCDBHelpers.cxx index df21738ddb647..53e6b66a2b30c 100644 --- a/Framework/CCDBSupport/test/test_CCDBHelpers.cxx +++ b/Framework/CCDBSupport/test/test_CCDBHelpers.cxx @@ -9,43 +9,39 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test Framework CCDBHelpers -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include -#include "../src/CCDBHelpers.h" +#include +#include "../src/CCDBFetcherHelper.h" using namespace o2::framework; -BOOST_AUTO_TEST_CASE(TestSorting) +TEST_CASE("TestSorting") { - auto result = CCDBHelpers::parseRemappings(""); - BOOST_CHECK_EQUAL(result.error, ""); // not an error + auto result = CCDBFetcherHelper::parseRemappings(""); + CHECK(result.error == ""); // not an error - result = CCDBHelpers::parseRemappings("https"); - BOOST_CHECK_EQUAL(result.error, "URL should start with either http:// or https:// or file://"); + result = CCDBFetcherHelper::parseRemappings("https"); + CHECK(result.error == "URL should start with either http:// or https:// or file://"); - result = CCDBHelpers::parseRemappings("https://alice.cern.ch:8000"); - BOOST_CHECK_EQUAL(result.error, "Expecting at least one target path, missing `='?"); + result = CCDBFetcherHelper::parseRemappings("https://alice.cern.ch:8000"); + CHECK(result.error == "Expecting at least one target path, missing `='?"); - result = CCDBHelpers::parseRemappings("https://alice.cern.ch:8000="); - BOOST_CHECK_EQUAL(result.error, "Empty target"); + result = CCDBFetcherHelper::parseRemappings("https://alice.cern.ch:8000="); + CHECK(result.error == "Empty target"); - result = CCDBHelpers::parseRemappings("https://alice.cern.ch:8000=/foo/bar,"); - BOOST_CHECK_EQUAL(result.error, "Empty target"); + result = CCDBFetcherHelper::parseRemappings("https://alice.cern.ch:8000=/foo/bar,"); + CHECK(result.error == "Empty target"); - result = CCDBHelpers::parseRemappings("https://alice.cern.ch:8000=/foo/bar,/foo/bar;"); - BOOST_CHECK_EQUAL(result.error, "URL should start with either http:// or https:// or file://"); + result = CCDBFetcherHelper::parseRemappings("https://alice.cern.ch:8000=/foo/bar,/foo/bar;"); + CHECK(result.error == "URL should start with either http:// or https:// or file://"); - result = CCDBHelpers::parseRemappings("https://alice.cern.ch:8000=/foo/bar,/foo/barbar;file://user/test=/foo/barr"); - BOOST_CHECK_EQUAL(result.error, ""); - BOOST_CHECK_EQUAL(result.remappings.size(), 3); - BOOST_CHECK_EQUAL(result.remappings["/foo/bar"], "https://alice.cern.ch:8000"); - BOOST_CHECK_EQUAL(result.remappings["/foo/barbar"], "https://alice.cern.ch:8000"); - BOOST_CHECK_EQUAL(result.remappings["/foo/barr"], "file://user/test"); + result = CCDBFetcherHelper::parseRemappings("https://alice.cern.ch:8000=/foo/bar,/foo/barbar;file://user/test=/foo/barr"); + CHECK(result.error == ""); + CHECK(result.remappings.size() == 3); + CHECK(result.remappings["/foo/bar"] == "https://alice.cern.ch:8000"); + CHECK(result.remappings["/foo/barbar"] == "https://alice.cern.ch:8000"); + CHECK(result.remappings["/foo/barr"] == "file://user/test"); - result = CCDBHelpers::parseRemappings("https://alice.cern.ch:8000=/foo/bar;file://user/test=/foo/bar"); - BOOST_CHECK_EQUAL(result.remappings.size(), 1); - BOOST_CHECK_EQUAL(result.error, "Path /foo/bar requested more than once."); + result = CCDBFetcherHelper::parseRemappings("https://alice.cern.ch:8000=/foo/bar;file://user/test=/foo/bar"); + CHECK(result.remappings.size() == 1); + CHECK(result.error == "Path /foo/bar requested more than once."); } diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 17320348d9272..e6a8db1077136 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -10,13 +10,13 @@ # or submit itself to any jurisdiction. o2_add_library(Framework - SOURCES src/AODReaderHelpers.cxx - src/AnalysisHelpers.cxx + SOURCES src/AnalysisHelpers.cxx src/AlgorithmSpec.cxx src/ArrowSupport.cxx src/ArrowTableSlicingCache.cxx src/AnalysisDataModel.cxx src/AnalysisSupportHelpers.cxx + src/AnalysisTask.cxx src/ASoA.cxx src/ASoAHelpers.cxx src/AsyncQueue.cxx @@ -87,6 +87,7 @@ o2_add_library(Framework src/FairMQDeviceProxy.cxx src/FairMQResizableBuffer.cxx src/FairOptionsRetriever.cxx + src/FragmentToBatch.cxx src/ConfigurationOptionsRetriever.cxx src/FreePortFinder.cxx src/GraphvizHelpers.cxx @@ -108,6 +109,7 @@ o2_add_library(Framework src/SimpleOptionsRetriever.cxx src/O2ControlHelpers.cxx src/O2ControlLabels.cxx + src/CommonLabels.cxx src/O2ControlParameters.cxx src/O2DataModelHelpers.cxx src/OutputSpec.cxx @@ -123,6 +125,7 @@ o2_add_library(Framework src/RootArrowFilesystem.cxx src/SendingPolicy.cxx src/ServiceRegistry.cxx + src/ServiceRegistryRef.cxx src/ServiceSpec.cxx src/SimpleResourceManager.cxx src/SimpleRawDeviceService.cxx @@ -131,8 +134,8 @@ o2_add_library(Framework src/TMessageSerializer.cxx src/TableBuilder.cxx src/TableConsumer.cxx - src/TableTreeHelpers.cxx src/TopologyPolicy.cxx + src/TopologyPolicyHelpers.cxx src/TextDriverClient.cxx src/TimesliceIndex.cxx src/TimingHelpers.cxx @@ -141,6 +144,8 @@ o2_add_library(Framework src/Array2D.cxx src/Variant.cxx src/VariantJSONHelpers.cxx + src/ExpressionJSONHelpers.cxx + src/IndexJSONHelpers.cxx src/VariantPropertyTreeHelpers.cxx src/WorkflowCustomizationHelpers.cxx src/WorkflowHelpers.cxx @@ -222,6 +227,7 @@ add_executable(o2-test-framework-core test/test_FairMQOptionsRetriever.cxx test/test_FairMQResizableBuffer.cxx test/test_FairMQ.cxx + test/test_ForwardInputs.cxx test/test_FrameworkDataFlowToDDS.cxx test/test_FrameworkDataFlowToO2Control.cxx test/test_Graphviz.cxx @@ -235,10 +241,12 @@ add_executable(o2-test-framework-core test/test_InputSpec.cxx test/test_LogParsingHelpers.cxx test/test_Mermaid.cxx + test/test_MessageSet.cxx test/test_OptionsHelpers.cxx test/test_OverrideLabels.cxx test/test_O2DataModelHelpers.cxx test/test_RootConfigParamHelpers.cxx + test/test_ResourcesMonitoringHelpers.cxx test/test_Services.cxx test/test_StringHelpers.cxx test/test_StaticFor.cxx @@ -248,6 +256,8 @@ add_executable(o2-test-framework-core test/test_TimeParallelPipelining.cxx test/test_TimesliceIndex.cxx test/test_TypeTraits.cxx + test/test_TypeToTaskName.cxx + test/test_TopologyPolicies.cxx test/test_Variants.cxx test/test_WorkflowHelpers.cxx test/test_WorkflowSerialization.cxx @@ -313,11 +323,14 @@ set_property(TARGET o2-test-framework-root PROPERTY RUNTIME_OUTPUT_DIRECTORY ${o add_test(NAME framework:root COMMAND o2-test-framework-root --skip-benchmarks) add_test(NAME framework:crash COMMAND sh -e -c "PATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}:$PATH ${CMAKE_CURRENT_LIST_DIR}/test/test_AllCrashTypes.sh") -o2_add_test(InfoLogger NAME test_Framework_test_InfoLogger - SOURCES test/test_InfoLogger.cxx - COMPONENT_NAME Framework - LABELS framework - PUBLIC_LINK_LIBRARIES O2::Framework AliceO2::InfoLogger) +add_executable(o2-test-framework-infologger + test/test_InfoLogger.cxx) +target_link_libraries(o2-test-framework-infologger PRIVATE O2::Framework) +target_link_libraries(o2-test-framework-infologger PRIVATE AliceO2::InfoLogger) +target_link_libraries(o2-test-framework-infologger PRIVATE O2::Catch2) +set_property(TARGET o2-test-framework-infologger + PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) +add_test(NAME framework:infologger COMMAND o2-test-framework-infologger) o2_add_executable(dpl-null-sink SOURCES src/o2NullSink.cxx @@ -334,6 +347,7 @@ o2_add_executable(dpl-run foreach(b DataDescriptorMatcher DataRelayer + Stack DeviceMetricsInfo InputRecord TableBuilder @@ -342,7 +356,6 @@ foreach(b ASoAHelpers EventMixing HistogramRegistry - TableToTree ExternalFairMQDeviceProxies ) o2_add_executable(benchmark-${b} diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 592864528244c..7586d6a6d3c63 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -12,6 +12,7 @@ #ifndef O2_FRAMEWORK_ASOA_H_ #define O2_FRAMEWORK_ASOA_H_ +#include "Framework/ConcreteDataMatcher.h" #include "Framework/Pack.h" // IWYU pragma: export #include "Framework/FunctionalHelpers.h" // IWYU pragma: export #include "Headers/DataHeader.h" // IWYU pragma: export @@ -23,17 +24,17 @@ #include "Framework/ArrowTableSlicingCache.h" // IWYU pragma: export #include "Framework/SliceCache.h" // IWYU pragma: export #include "Framework/VariantHelpers.h" // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #include #include #include #include #include // IWYU pragma: export -#include namespace o2::framework { @@ -43,12 +44,21 @@ std::string cutString(std::string&& str); std::string strToUpper(std::string&& str); } // namespace o2::framework +struct TClass; + namespace o2::soa { void accessingInvalidIndexFor(const char* getter); void dereferenceWithWrongType(const char* getter, const char* target); void missingFilterDeclaration(int hash, int ai); void notBoundTable(const char* tableName); +void* extractCCDBPayload(char* payload, size_t size, TClass const* cl, const char* what); + +template +auto createFieldsFromColumns(framework::pack) +{ + return std::vector>{C::asArrowField()...}; +} } // namespace o2::soa namespace o2::soa @@ -208,6 +218,20 @@ using is_self_index_t = typename std::conditional_t, std namespace o2::aod { +namespace +{ +template map> +static consteval int getIndexPosToKey_impl() +{ + constexpr const auto pos = std::find(map.begin(), map.end(), true); + if constexpr (pos != map.end()) { + return std::distance(map.begin(), pos); + } else { + return -1; + } +} +} // namespace + /// Base type for table metadata template struct TableMetadata { @@ -234,15 +258,9 @@ struct TableMetadata { return getIndexPosToKey_impl(persistent_columns_t{})>(); } - template map> - static consteval int getIndexPosToKey_impl() + static std::shared_ptr getSchema() { - constexpr const auto pos = std::find(map.begin(), map.end(), true); - if constexpr (pos != map.end()) { - return std::distance(map.begin(), pos); - } else { - return -1; - } + return std::make_shared([](framework::pack&& p) { return o2::soa::createFieldsFromColumns(p); }(persistent_columns_t{})); } }; @@ -358,6 +376,12 @@ consteval const char* signature() return o2::aod::Hash::str; } +template +constexpr framework::ConcreteDataMatcher matcher() +{ + return {origin(), description(signature()), R.version}; +} + /// hash identification concepts template concept is_aod_hash = requires(T t) { t.hash; t.str; }; @@ -381,15 +405,15 @@ class Table; /// Type-checking index column binding struct Binding { void const* ptr = nullptr; - size_t hash = 0; - std::span refs; + uint32_t hash = 0; + // std::span refs; template void bind(T const* table) { ptr = table; hash = o2::framework::TypeIdHelpers::uniqueId(); - refs = std::span{T::originals}; + // refs = std::span{T::originals}; } template @@ -402,12 +426,6 @@ struct Binding { } }; -template -auto createFieldsFromColumns(framework::pack) -{ - return std::vector>{C::asArrowField()...}; -} - using SelectionVector = std::vector; template @@ -560,25 +578,33 @@ class ColumnIterator : ChunkingPolicy mLast = mCurrent + array->length() + (mFirstIndex >> SCALE_FACTOR); } - decltype(auto) operator*() const + auto operator*() const requires std::same_as> { checkSkipChunk(); return (*(mCurrent - (mOffset >> SCALE_FACTOR) + ((*mCurrentPos + mOffset) >> SCALE_FACTOR)) & (1 << ((*mCurrentPos + mOffset) & 0x7))) != 0; } - decltype(auto) operator*() const + auto operator*() const requires((!std::same_as>) && std::same_as, arrow::ListArray>) { checkSkipChunk(); auto list = std::static_pointer_cast(mColumn->chunk(mCurrentChunk)); auto offset = list->value_offset(*mCurrentPos - mFirstIndex); auto length = list->value_length(*mCurrentPos - mFirstIndex); - return gsl::span{mCurrent + mFirstIndex + offset, mCurrent + mFirstIndex + (offset + length)}; + return gsl::span const>{mCurrent + mFirstIndex + offset, mCurrent + mFirstIndex + (offset + length)}; } decltype(auto) operator*() const - requires((!std::same_as>) && !std::same_as, arrow::ListArray>) + requires((!std::same_as>) && std::same_as, arrow::BinaryViewArray>) + { + checkSkipChunk(); + auto array = std::static_pointer_cast(mColumn->chunk(mCurrentChunk)); + return array->GetView(*mCurrentPos - mFirstIndex); + } + + decltype(auto) operator*() const + requires((!std::same_as>) && !std::same_as, arrow::ListArray> && !std::same_as, arrow::BinaryViewArray>) { checkSkipChunk(); return *(mCurrent + (*mCurrentPos >> SCALE_FACTOR)); @@ -674,7 +700,7 @@ struct Column { static auto asArrowField() { - return std::make_shared(inherited_t::mLabel, framework::expressions::concreteArrowType(framework::expressions::selectArrowType())); + return std::make_shared(inherited_t::mLabel, soa::asArrowDataType()); } /// FIXME: rather than keeping this public we should have a protected @@ -694,6 +720,7 @@ struct DynamicColumn { template struct IndexColumn { using inherited_t = INHERIT; + static constexpr const uint32_t hash = 0; static constexpr const char* const& columnLabel() { return INHERIT::mLabel; } }; @@ -701,6 +728,7 @@ struct IndexColumn { template struct MarkerColumn { using inherited_t = INHERIT; + static constexpr const uint32_t hash = 0; static constexpr const char* const& columnLabel() { return INHERIT::mLabel; } }; @@ -840,7 +868,7 @@ struct FilteredIndexPolicy : IndexPolicyBase { // which happens below which will properly setup the first index // by remapping the filtered index 0 to whatever unfiltered index // it belongs to. - FilteredIndexPolicy(gsl::span selection, int64_t rows, uint64_t offset = 0) + FilteredIndexPolicy(std::span selection, int64_t rows, uint64_t offset = 0) : IndexPolicyBase{-1, offset}, mSelectedRows(selection), mMaxSelection(selection.size()), @@ -849,7 +877,7 @@ struct FilteredIndexPolicy : IndexPolicyBase { this->setCursor(0); } - void resetSelection(gsl::span selection) + void resetSelection(std::span selection) { mSelectedRows = selection; mMaxSelection = selection.size(); @@ -933,7 +961,7 @@ struct FilteredIndexPolicy : IndexPolicyBase { { this->mRowIndex = O2_BUILTIN_LIKELY(mSelectionRow < mMaxSelection) ? mSelectedRows[mSelectionRow] : -1; } - gsl::span mSelectedRows; + std::span mSelectedRows; int64_t mSelectionRow = 0; int64_t mMaxSelection = 0; int64_t nRows = 0; @@ -1054,7 +1082,9 @@ struct TableIterator : IP, C... { : IP{policy}, C(columnData[framework::has_type_at_v(all_columns{})])... { - bind(); + if (this->size() != 0) { + bind(); + } } TableIterator(arrow::ChunkedArray* columnData[sizeof...(C)], IP&& policy) @@ -1062,7 +1092,9 @@ struct TableIterator : IP, C... { : IP{policy}, C(columnData[framework::has_type_at_v(all_columns{})])... { - bind(); + if (this->size() != 0) { + bind(); + } // In case we have an index column might need to constrain the actual // number of rows in the view to the range provided by the index. // FIXME: we should really understand what happens to an index when we @@ -1075,14 +1107,18 @@ struct TableIterator : IP, C... { : IP{static_cast(other)}, C(static_cast(other))... { - bind(); + if (this->size() != 0) { + bind(); + } } TableIterator& operator=(TableIterator other) { IP::operator=(static_cast(other)); (void(static_cast(*this) = static_cast(other)), ...); - bind(); + if (this->size() != 0) { + bind(); + } return *this; } @@ -1091,7 +1127,9 @@ struct TableIterator : IP, C... { : IP{static_cast(other)}, C(static_cast(other))... { - bind(); + if (this->size() != 0) { + bind(); + } } TableIterator& operator++() @@ -1245,7 +1283,9 @@ struct TableIterator : IP, C... { }; struct ArrowHelpers { + static std::shared_ptr joinTables(std::vector>&& tables); static std::shared_ptr joinTables(std::vector>&& tables, std::span labels); + static std::shared_ptr joinTables(std::vector>&& tables, std::span labels); static std::shared_ptr concatTables(std::vector>&& tables); }; @@ -1253,6 +1293,9 @@ struct ArrowHelpers { template concept is_iterator = framework::base_of_template || framework::specialization_of_template; +template +concept is_table_or_iterator = is_table || is_iterator; + template concept with_originals = requires { T::originals.size(); @@ -1264,7 +1307,24 @@ concept with_sources = requires { }; template -concept with_base_table = not_void>::metadata::base_table_t>; +concept with_ccdb_urls = requires { + T::ccdb_urls.size(); +}; + +template +concept with_base_table = requires { + typename aod::MetadataTrait>::metadata::base_table_t; +}; + +template +concept with_expression_pack = requires { + typename T::expression_pack_t{}; +}; + +template +concept with_index_pack = requires { + typename T::index_pack_t{}; +}; template os1, size_t N2, std::array os2> consteval bool is_compatible() @@ -1327,7 +1387,15 @@ static constexpr std::string getLabelFromType() template static constexpr auto hasColumnForKey(framework::pack, std::string const& key) { - return ((C::inherited_t::mLabel == key) || ...); + auto caseInsensitiveCompare = [](const std::string_view& str1, const std::string& str2) { + return std::ranges::equal( + str1, str2, + [](char c1, char c2) { + return std::tolower(static_cast(c1)) == + std::tolower(static_cast(c2)); + }); + }; + return (caseInsensitiveCompare(C::inherited_t::mLabel, key) || ...); } template @@ -1336,6 +1404,12 @@ static constexpr std::pair hasKey(std::string const& key) return {hasColumnForKey(typename aod::MetadataTrait>::metadata::columns{}, key), aod::label()}; } +template +static constexpr std::pair hasKeyM(std::string const& key) +{ + return {hasColumnForKey(typename aod::MetadataTrait>::metadata::columns{}, key), aod::matcher()}; +} + template static constexpr auto haveKey(framework::pack, std::string const& key) { @@ -1370,6 +1444,31 @@ static constexpr std::string getLabelFromTypeForKey(std::string const& key) O2_BUILTIN_UNREACHABLE(); } +template +static constexpr framework::ConcreteDataMatcher getMatcherFromTypeForKey(std::string const& key) +{ + if constexpr (T::originals.size() == 1) { + auto locate = hasKeyM(key); + if (locate.first) { + return locate.second; + } + } else { + auto locate = [&](std::index_sequence) { + return std::vector{hasKeyM(key)...}; + }(std::make_index_sequence{}); + auto it = std::find_if(locate.begin(), locate.end(), [](auto const& x) { return x.first; }); + if (it != locate.end()) { + return it->second; + } + } + if constexpr (!OPT) { + notFoundColumn(getLabelFromType>().data(), key.data()); + } else { + return framework::ConcreteDataMatcher{header::DataOrigin{"AOD"}, header::DataDescription{"[MISSING]"}, 0}; + } + O2_BUILTIN_UNREACHABLE(); +} + template consteval static bool hasIndexTo(framework::pack&&) { @@ -1417,10 +1516,13 @@ struct PreslicePolicyGeneral : public PreslicePolicyBase { void updateSliceInfo(SliceInfoUnsortedPtr&& si); SliceInfoUnsortedPtr sliceInfo; - gsl::span getSliceFor(int value) const; + std::span getSliceFor(int value) const; }; -template +template +concept is_preslice_policy = std::derived_from; + +template struct PresliceBase : public Policy { constexpr static bool optional = OPT; using target_t = T; @@ -1428,7 +1530,7 @@ struct PresliceBase : public Policy { const std::string binding; PresliceBase(expressions::BindingNode index_) - : Policy{PreslicePolicyBase{{o2::soa::getLabelFromTypeForKey(std::string{index_.name})}, Entry(o2::soa::getLabelFromTypeForKey(std::string{index_.name}), std::string{index_.name})}, {}} + : Policy{PreslicePolicyBase{{o2::soa::getLabelFromTypeForKey(std::string{index_.name})}, Entry(o2::soa::getLabelFromTypeForKey(std::string{index_.name}), o2::soa::getMatcherFromTypeForKey(std::string{index_.name}), std::string{index_.name})}, {}} { } @@ -1442,7 +1544,7 @@ struct PresliceBase : public Policy { return Policy::getSliceFor(value, input, offset); } - gsl::span getSliceFor(int value) const + std::span getSliceFor(int value) const { if constexpr (OPT) { if (Policy::isMissing()) { @@ -1463,7 +1565,29 @@ template using PresliceOptional = PresliceBase; template -concept is_preslice = std::derived_from; +concept is_preslice = std::derived_from&& + requires(T) +{ + T::optional; +}; + +/// Can be user to group together a number of Preslice declaration +/// to avoid the limit of 100 data members per task +/// +/// struct MyTask +/// struct : public PresliceGroup { +/// Preslice perCol = aod::track::collisonId; +/// Preslice perMcCol = aod::mcparticle::mcCollisionId; +/// } preslices; +/// +/// individual components can be access with +/// +/// preslices.perCol; +struct PresliceGroup { +}; + +template +concept is_preslice_group = std::derived_from; } // namespace o2::framework @@ -1514,28 +1638,34 @@ auto doSliceBy(T const* table, o2::framework::PresliceBase const uint64_t offset = 0; auto out = container.getSliceFor(value, table->asArrowTable(), offset); auto t = typename T::self_t({out}, offset); - table->copyIndexBindings(t); - t.bindInternalIndicesTo(table); + if (t.tableSize() != 0) { + table->copyIndexBindings(t); + t.bindInternalIndicesTo(table); + } return t; } template -auto doSliceByHelper(T const* table, gsl::span const& selection) +auto doSliceByHelper(T const* table, std::span const& selection) { auto t = soa::Filtered({table->asArrowTable()}, selection); - table->copyIndexBindings(t); - t.bindInternalIndicesTo(table); - t.intersectWithSelection(table->getSelectedRows()); // intersect filters + if (t.tableSize() != 0) { + table->copyIndexBindings(t); + t.bindInternalIndicesTo(table); + t.intersectWithSelection(table->getSelectedRows()); // intersect filters + } return t; } template requires(!soa::is_filtered_table) -auto doSliceByHelper(T const* table, gsl::span const& selection) +auto doSliceByHelper(T const* table, std::span const& selection) { auto t = soa::Filtered({table->asArrowTable()}, selection); - table->copyIndexBindings(t); - t.bindInternalIndicesTo(table); + if (t.tableSize() != 0) { + table->copyIndexBindings(t); + t.bindInternalIndicesTo(table); + } return t; } @@ -1552,19 +1682,23 @@ auto doSliceBy(T const* table, o2::framework::PresliceBase const return doSliceByHelper(table, selection); } -SelectionVector sliceSelection(gsl::span const& mSelectedRows, int64_t nrows, uint64_t offset); +SelectionVector sliceSelection(std::span const& mSelectedRows, int64_t nrows, uint64_t offset); template auto prepareFilteredSlice(T const* table, std::shared_ptr slice, uint64_t offset) { if (offset >= static_cast(table->tableSize())) { Filtered fresult{{{slice}}, SelectionVector{}, 0}; - table->copyIndexBindings(fresult); + if (fresult.tableSize() != 0) { + table->copyIndexBindings(fresult); + } return fresult; } auto slicedSelection = sliceSelection(table->getSelectedRows(), slice->num_rows(), offset); Filtered fresult{{{slice}}, std::move(slicedSelection), offset}; - table->copyIndexBindings(fresult); + if (fresult.tableSize() != 0) { + table->copyIndexBindings(fresult); + } return fresult; } @@ -1582,37 +1716,43 @@ auto doFilteredSliceBy(T const* table, o2::framework::PresliceBase +template auto doSliceByCached(T const* table, framework::expressions::BindingNode const& node, int value, o2::framework::SliceCache& cache) { - auto localCache = cache.ptr->getCacheFor({o2::soa::getLabelFromTypeForKey(node.name), node.name}); + auto localCache = cache.ptr->getCacheFor({"", o2::soa::getMatcherFromTypeForKey(node.name), node.name}); auto [offset, count] = localCache.getSliceFor(value); auto t = typename T::self_t({table->asArrowTable()->Slice(static_cast(offset), count)}, static_cast(offset)); - table->copyIndexBindings(t); + if (t.tableSize() != 0) { + table->copyIndexBindings(t); + } return t; } -template +template auto doFilteredSliceByCached(T const* table, framework::expressions::BindingNode const& node, int value, o2::framework::SliceCache& cache) { - auto localCache = cache.ptr->getCacheFor({o2::soa::getLabelFromTypeForKey(node.name), node.name}); + auto localCache = cache.ptr->getCacheFor({"", o2::soa::getMatcherFromTypeForKey(node.name), node.name}); auto [offset, count] = localCache.getSliceFor(value); auto slice = table->asArrowTable()->Slice(static_cast(offset), count); return prepareFilteredSlice(table, slice, offset); } -template +template auto doSliceByCachedUnsorted(T const* table, framework::expressions::BindingNode const& node, int value, o2::framework::SliceCache& cache) { - auto localCache = cache.ptr->getCacheUnsortedFor({o2::soa::getLabelFromTypeForKey(node.name), node.name}); + auto localCache = cache.ptr->getCacheUnsortedFor({"", o2::soa::getMatcherFromTypeForKey(node.name), node.name}); if constexpr (soa::is_filtered_table) { auto t = typename T::self_t({table->asArrowTable()}, localCache.getSliceFor(value)); - t.intersectWithSelection(table->getSelectedRows()); - table->copyIndexBindings(t); + if (t.tableSize() != 0) { + t.intersectWithSelection(table->getSelectedRows()); + table->copyIndexBindings(t); + } return t; } else { auto t = Filtered({table->asArrowTable()}, localCache.getSliceFor(value)); - table->copyIndexBindings(t); + if (t.tableSize() != 0) { + table->copyIndexBindings(t); + } return t; } } @@ -1907,7 +2047,7 @@ class Table static constexpr auto hashes() { - return [](framework::pack) { return std::set{{o2::framework::TypeIdHelpers::uniqueId()...}}; }(columns_t{}); + return [](framework::pack) { return std::set{{C::hash...}}; }(columns_t{}); } Table(std::shared_ptr table, uint64_t offset = 0) @@ -1982,7 +2122,7 @@ class Table return RowViewSentinel{mEnd}; } - filtered_iterator filtered_begin(gsl::span selection) + filtered_iterator filtered_begin(std::span selection) { // Note that the FilteredIndexPolicy will never outlive the selection which // is held by the table, so we are safe passing the bare pointer. If it does it @@ -2136,61 +2276,14 @@ void emptyColumnLabel(); namespace row_helpers { -template -std::array getArrowColumns(arrow::Table* table, framework::pack) -{ - return std::array{o2::soa::getIndexFromLabel(table, Cs::columnLabel())...}; -} - -template -std::array, sizeof...(Cs)> getChunks(arrow::Table* table, framework::pack, uint64_t ci) -{ - return std::array, sizeof...(Cs)>{o2::soa::getIndexFromLabel(table, Cs::columnLabel())->chunk(ci)...}; -} - -template -typename C::type getSingleRowData(arrow::Table* table, T& rowIterator, uint64_t ci = std::numeric_limits::max(), uint64_t ai = std::numeric_limits::max(), uint64_t globalIndex = std::numeric_limits::max()) -{ - if (ci == std::numeric_limits::max() || ai == std::numeric_limits::max()) { - auto colIterator = static_cast(rowIterator).getIterator(); - ci = colIterator.mCurrentChunk; - ai = *(colIterator.mCurrentPos) - colIterator.mFirstIndex; - } - return std::static_pointer_cast>(o2::soa::getIndexFromLabel(table, C::columnLabel())->chunk(ci))->raw_values()[ai]; -} - -template -typename C::type getSingleRowData(arrow::Table*, T& rowIterator, uint64_t ci = std::numeric_limits::max(), uint64_t ai = std::numeric_limits::max(), uint64_t globalIndex = std::numeric_limits::max()) -{ - if (globalIndex != std::numeric_limits::max() && globalIndex != *std::get<0>(rowIterator.getIndices())) { - rowIterator.setCursor(globalIndex); - } - return rowIterator.template getDynamicColumn(); -} - -template -typename C::type getSingleRowData(arrow::Table*, T& rowIterator, uint64_t ci = std::numeric_limits::max(), uint64_t ai = std::numeric_limits::max(), uint64_t globalIndex = std::numeric_limits::max()) -{ - if (globalIndex != std::numeric_limits::max() && globalIndex != *std::get<0>(rowIterator.getIndices())) { - rowIterator.setCursor(globalIndex); - } - return rowIterator.template getId(); -} - -template -std::tuple getRowData(arrow::Table* table, T rowIterator, uint64_t ci = std::numeric_limits::max(), uint64_t ai = std::numeric_limits::max(), uint64_t globalIndex = std::numeric_limits::max()) -{ - return std::make_tuple(getSingleRowData(table, rowIterator, ci, ai, globalIndex)...); -} - -namespace -{ template R getColumnValue(const T& rowIterator) { return static_cast(static_cast(rowIterator).get()); } +namespace +{ template using ColumnGetterFunction = R (*)(const T&); @@ -2266,11 +2359,14 @@ ColumnGetterFunction getColumnGetterByLabel(const std:: namespace o2::aod { +// If you get an error about not satisfying is_origin_hash, you need to add +// an entry here. O2ORIGIN("AOD"); O2ORIGIN("AOD1"); O2ORIGIN("AOD2"); O2ORIGIN("DYN"); O2ORIGIN("IDX"); +O2ORIGIN("ATIM"); O2ORIGIN("JOIN"); O2HASH("JOIN/0"); O2ORIGIN("CONC"); @@ -2279,6 +2375,17 @@ O2ORIGIN("TEST"); O2HASH("TEST/0"); } // namespace o2::aod +namespace +{ +template +consteval static std::string_view namespace_prefix() +{ + constexpr auto name = o2::framework::type_name(); + const auto pos = name.rfind(std::string_view{":"}); + return name.substr(0, pos + 1); +} +} // namespace + #define DECLARE_EQUIVALENT_FOR_INDEX(_Base_, _Equiv_) \ template <> \ struct EquivalentIndexNG, o2::aod::Hash<_Equiv_::ref.desc_hash>> { \ @@ -2294,6 +2401,7 @@ O2HASH("TEST/0"); #define DECLARE_SOA_COLUMN_FULL(_Name_, _Getter_, _Type_, _Label_) \ struct _Name_ : o2::soa::Column<_Type_, _Name_> { \ static constexpr const char* mLabel = _Label_; \ + static constexpr const uint32_t hash = compile_time_hash(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ static_assert(!((*(mLabel + 1) == 'I' && *(mLabel + 2) == 'n' && *(mLabel + 3) == 'd' && *(mLabel + 4) == 'e' && *(mLabel + 5) == 'x')), "Index is not a valid column name"); \ using base = o2::soa::Column<_Type_, _Name_>; \ using type = _Type_; \ @@ -2317,8 +2425,49 @@ O2HASH("TEST/0"); return _Getter_(); \ } \ }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, o2::framework::TypeIdHelpers::uniqueId<_Name_>(), \ - o2::framework::expressions::selectArrowType<_Type_>() } + [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType<_Type_>() } + +#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_) \ + struct _Name_ : o2::soa::Column, _Name_> { \ + static constexpr const char* mLabel = _Label_; \ + static constexpr const char* query = _CCDBQuery_; \ + static constexpr const uint32_t hash = crc32(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ + using base = o2::soa::Column, _Name_>; \ + using type = std::span; \ + using column_t = _Name_; \ + _Name_(arrow::ChunkedArray const* column) \ + : o2::soa::Column, _Name_>(o2::soa::ColumnIterator>(column)) \ + { \ + } \ + \ + _Name_() = default; \ + _Name_(_Name_ const& other) = default; \ + _Name_& operator=(_Name_ const& other) = default; \ + \ + decltype(auto) _Getter_() const \ + { \ + static std::byte* payload = nullptr; \ + static _ConcreteType_* deserialised = nullptr; \ + static TClass* c = TClass::GetClass(#_ConcreteType_); \ + auto span = *mColumnIterator; \ + if (payload != (std::byte*)span.data()) { \ + payload = (std::byte*)span.data(); \ + delete deserialised; \ + TBufferFile f(TBufferFile::EMode::kRead, span.size(), (char*)span.data(), kFALSE); \ + deserialised = (_ConcreteType_*)soa::extractCCDBPayload((char*)payload, span.size(), c, "ccdb_object"); \ + } \ + return *deserialised; \ + } \ + \ + decltype(auto) \ + get() const \ + { \ + return _Getter_(); \ + } \ + }; + +#define DECLARE_SOA_CCDB_COLUMN(_Name_, _Getter_, _ConcreteType_, _CCDBQuery_) \ + DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, "f" #_Name_, _Getter_, _ConcreteType_, _CCDBQuery_) #define DECLARE_SOA_COLUMN(_Name_, _Getter_, _Type_) \ DECLARE_SOA_COLUMN_FULL(_Name_, _Getter_, _Type_, "f" #_Name_) @@ -2330,6 +2479,7 @@ O2HASH("TEST/0"); #define DECLARE_SOA_BITMAP_COLUMN_FULL(_Name_, _Getter_, _Size_, _Label_) \ struct _Name_ : o2::soa::Column { \ static constexpr const char* mLabel = _Label_; \ + static constexpr const uint32_t hash = compile_time_hash(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ static_assert(!((*(mLabel + 1) == 'I' && *(mLabel + 2) == 'n' && *(mLabel + 3) == 'd' && *(mLabel + 4) == 'e' && *(mLabel + 5) == 'x')), "Index is not a valid column name"); \ using base = o2::soa::Column; \ using type = MAKEINT(_Size_); \ @@ -2352,82 +2502,81 @@ O2HASH("TEST/0"); return (*mColumnIterator & (static_cast(1) << bit)) >> bit; \ } \ }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, o2::framework::TypeIdHelpers::uniqueId<_Name_>(), \ - o2::framework::expressions::selectArrowType() } + [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType() } #define DECLARE_SOA_BITMAP_COLUMN(_Name_, _Getter_, _Size_) \ DECLARE_SOA_BITMAP_COLUMN_FULL(_Name_, _Getter_, _Size_, "f" #_Name_) /// An 'expression' column. i.e. a column that can be calculated from other /// columns with gandiva based on static C++ expression. -#define DECLARE_SOA_EXPRESSION_COLUMN_FULL(_Name_, _Getter_, _Type_, _Label_, _Expression_) \ - struct _Name_ : o2::soa::Column<_Type_, _Name_> { \ - static constexpr const char* mLabel = _Label_; \ - using base = o2::soa::Column<_Type_, _Name_>; \ - using type = _Type_; \ - using column_t = _Name_; \ - using spawnable_t = std::true_type; \ - _Name_(arrow::ChunkedArray const* column) \ - : o2::soa::Column<_Type_, _Name_>(o2::soa::ColumnIterator(column)) \ - { \ - } \ - \ - _Name_() = default; \ - _Name_(_Name_ const& other) = default; \ - _Name_& operator=(_Name_ const& other) = default; \ - \ - decltype(auto) _Getter_() const \ - { \ - return *mColumnIterator; \ - } \ - \ - decltype(auto) get() const \ - { \ - return _Getter_(); \ - } \ - \ - static o2::framework::expressions::Projector Projector() \ - { \ - return _Expression_; \ - } \ - }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, o2::framework::TypeIdHelpers::uniqueId<_Name_>(), \ - o2::framework::expressions::selectArrowType<_Type_>() } +#define DECLARE_SOA_EXPRESSION_COLUMN_FULL(_Name_, _Getter_, _Type_, _Label_, _Expression_) \ + struct _Name_ : o2::soa::Column<_Type_, _Name_> { \ + static constexpr const char* mLabel = _Label_; \ + static constexpr const uint32_t hash = compile_time_hash(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ + using base = o2::soa::Column<_Type_, _Name_>; \ + using type = _Type_; \ + using column_t = _Name_; \ + using spawnable_t = std::true_type; \ + _Name_(arrow::ChunkedArray const* column) \ + : o2::soa::Column<_Type_, _Name_>(o2::soa::ColumnIterator(column)) \ + { \ + } \ + \ + _Name_() = default; \ + _Name_(_Name_ const& other) = default; \ + _Name_& operator=(_Name_ const& other) = default; \ + \ + decltype(auto) _Getter_() const \ + { \ + return *mColumnIterator; \ + } \ + \ + decltype(auto) get() const \ + { \ + return _Getter_(); \ + } \ + \ + static o2::framework::expressions::Projector Projector() \ + { \ + return _Expression_; \ + } \ + }; \ + [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType<_Type_>() } #define DECLARE_SOA_EXPRESSION_COLUMN(_Name_, _Getter_, _Type_, _Expression_) \ DECLARE_SOA_EXPRESSION_COLUMN_FULL(_Name_, _Getter_, _Type_, "f" #_Name_, _Expression_); /// A configurable 'expression' column. i.e. a column that can be calculated from other /// columns with gandiva based on dynamically supplied C++ expression or a string definition. -#define DECLARE_SOA_CONFIGURABLE_EXPRESSION_COLUMN(_Name_, _Getter_, _Type_, _Label_) \ - struct _Name_ : o2::soa::Column<_Type_, _Name_> { \ - static constexpr const char* mLabel = _Label_; \ - static constexpr const int32_t mHash = _Label_ ""_h; \ - using base = o2::soa::Column<_Type_, _Name_>; \ - using type = _Type_; \ - using column_t = _Name_; \ - using spawnable_t = std::true_type; \ - _Name_(arrow::ChunkedArray const* column) \ - : o2::soa::Column<_Type_, _Name_>(o2::soa::ColumnIterator(column)) \ - { \ - } \ - \ - _Name_() = default; \ - _Name_(_Name_ const& other) = default; \ - _Name_& operator=(_Name_ const& other) = default; \ - \ - decltype(auto) _Getter_() const \ - { \ - return *mColumnIterator; \ - } \ - \ - decltype(auto) get() const \ - { \ - return _Getter_(); \ - } \ - }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, o2::framework::TypeIdHelpers::uniqueId<_Name_>(), \ - o2::framework::expressions::selectArrowType<_Type_>() } +#define DECLARE_SOA_CONFIGURABLE_EXPRESSION_COLUMN(_Name_, _Getter_, _Type_, _Label_) \ + struct _Name_ : o2::soa::Column<_Type_, _Name_> { \ + static constexpr const char* mLabel = _Label_; \ + static constexpr const uint32_t hash = compile_time_hash(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ + static constexpr const int32_t mHash = _Label_ ""_h; \ + using base = o2::soa::Column<_Type_, _Name_>; \ + using type = _Type_; \ + using column_t = _Name_; \ + using spawnable_t = std::true_type; \ + _Name_(arrow::ChunkedArray const* column) \ + : o2::soa::Column<_Type_, _Name_>(o2::soa::ColumnIterator(column)) \ + { \ + } \ + \ + _Name_() = default; \ + _Name_(_Name_ const& other) = default; \ + _Name_& operator=(_Name_ const& other) = default; \ + \ + decltype(auto) _Getter_() const \ + { \ + return *mColumnIterator; \ + } \ + \ + decltype(auto) get() const \ + { \ + return _Getter_(); \ + } \ + }; \ + [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType<_Type_>() } /// An index column is a column of indices to elements / of another table named /// _Name_##s. The column name will be _Name_##Id and will always be stored in @@ -2461,6 +2610,7 @@ consteval auto getIndexTargets() static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ static_assert((*_Suffix_ == '\0') || (*_Suffix_ == '_'), "Suffix has to begin with _"); \ static constexpr const char* mLabel = "fIndexSlice" _Label_ _Suffix_; \ + static constexpr const uint32_t hash = 0; \ using base = o2::soa::Column<_Type_[2], _Name_##IdSlice>; \ using type = _Type_[2]; \ using column_t = _Name_##IdSlice; \ @@ -2547,6 +2697,7 @@ consteval auto getIndexTargets() static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ static_assert((*_Suffix_ == '\0') || (*_Suffix_ == '_'), "Suffix has to begin with _"); \ static constexpr const char* mLabel = "fIndexArray" _Label_ _Suffix_; \ + static constexpr const uint32_t hash = 0; \ using base = o2::soa::Column, _Name_##Ids>; \ using type = std::vector<_Type_>; \ using column_t = _Name_##Ids; \ @@ -2576,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2586,10 +2737,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getIterators(); \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto& i : *mColumnIterator) { \ + result.emplace_back(t->rawIteratorAt(i)); \ + } \ + return result; \ } \ \ - template \ + template \ auto filtered_##_Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2599,35 +2755,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getFilteredIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ - auto result = std::vector(); \ - for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ - } \ - return result; \ - } \ - \ - template \ - std::vector getFilteredIterators() const \ - { \ - if constexpr (o2::soa::is_filtered_table) { \ - auto result = std::vector(); \ - for (auto const& i : *mColumnIterator) { \ - auto pos = mBinding.get()->isInSelectedRows(i); \ - if (pos > 0) { \ - result.emplace_back(mBinding.get()->iteratorAt(pos)); \ - } \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto const& i : *mColumnIterator) { \ + auto pos = t->isInSelectedRows(i); \ + if (pos > 0) { \ + result.emplace_back(t->iteratorAt(pos)); \ } \ - return result; \ - } else { \ - static_assert(o2::framework::always_static_assert_v, "T is not a Filtered type"); \ } \ - return {}; \ + return result; \ } \ \ auto _Getter_() const \ @@ -2697,143 +2833,143 @@ consteval auto getIndexTargets() #define DECLARE_SOA_ARRAY_INDEX_COLUMN_CUSTOM(_Name_, _Getter_, _Label_) DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL_CUSTOM(_Name_, _Getter_, int32_t, _Name_##s, _Label_, "") /// NORMAL -#define DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(_Name_, _Getter_, _Type_, _Table_, _Label_, _Suffix_) \ - struct _Name_##Id : o2::soa::Column<_Type_, _Name_##Id> { \ - static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ - static_assert((*_Suffix_ == '\0') || (*_Suffix_ == '_'), "Suffix has to begin with _"); \ - static constexpr const char* mLabel = "fIndex" _Label_ _Suffix_; \ - using base = o2::soa::Column<_Type_, _Name_##Id>; \ - using type = _Type_; \ - using column_t = _Name_##Id; \ - using binding_t = _Table_; \ - static constexpr auto index_targets = getIndexTargets<_Table_>(); \ - _Name_##Id(arrow::ChunkedArray const* column) \ - : o2::soa::Column<_Type_, _Name_##Id>(o2::soa::ColumnIterator(column)) \ - { \ - } \ - \ - _Name_##Id() = default; \ - _Name_##Id(_Name_##Id const& other) = default; \ - _Name_##Id& operator=(_Name_##Id const& other) = default; \ - type inline getId() const \ - { \ - return _Getter_##Id(); \ - } \ - \ - type _Getter_##Id() const \ - { \ - return *mColumnIterator; \ - } \ - \ - bool has_##_Getter_() const \ - { \ - return *mColumnIterator >= 0; \ - } \ - \ - template \ - auto _Getter_##_as() const \ - { \ - if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ - o2::soa::notBoundTable(#_Table_); \ - } \ - if (O2_BUILTIN_UNLIKELY(!has_##_Getter_())) { \ - o2::soa::accessingInvalidIndexFor(#_Getter_); \ - } \ - auto t = mBinding.get(); \ - if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ - o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ - } \ - return t->rawIteratorAt(*mColumnIterator); \ - } \ - \ - auto _Getter_() const \ - { \ - return _Getter_##_as(); \ - } \ - \ - template \ - bool setCurrent(T* current) \ - { \ - if constexpr (o2::soa::is_binding_compatible_v()) { \ - assert(current != nullptr); \ - this->mBinding.bind(current); \ - return true; \ - } \ - return false; \ - } \ - \ - bool setCurrentRaw(o2::soa::Binding current) \ - { \ - this->mBinding = current; \ - return true; \ - } \ - binding_t const* getCurrent() const { return mBinding.get(); } \ - o2::soa::Binding getCurrentRaw() const { return mBinding; } \ - o2::soa::Binding mBinding; \ - }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_##Id { "fIndex" #_Table_ _Suffix_, o2::framework::TypeIdHelpers::uniqueId<_Name_##Id>(), \ - o2::framework::expressions::selectArrowType<_Type_>() } +#define DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(_Name_, _Getter_, _Type_, _Table_, _Label_, _Suffix_) \ + struct _Name_##Id : o2::soa::Column<_Type_, _Name_##Id> { \ + static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ + static_assert((*_Suffix_ == '\0') || (*_Suffix_ == '_'), "Suffix has to begin with _"); \ + static constexpr const char* mLabel = "fIndex" _Label_ _Suffix_; \ + static constexpr const uint32_t hash = compile_time_hash(namespace_prefix<_Name_##Id>(), std::string_view{#_Getter_ "Id"}); \ + using base = o2::soa::Column<_Type_, _Name_##Id>; \ + using type = _Type_; \ + using column_t = _Name_##Id; \ + using binding_t = _Table_; \ + static constexpr auto index_targets = getIndexTargets<_Table_>(); \ + _Name_##Id(arrow::ChunkedArray const* column) \ + : o2::soa::Column<_Type_, _Name_##Id>(o2::soa::ColumnIterator(column)) \ + { \ + } \ + \ + _Name_##Id() = default; \ + _Name_##Id(_Name_##Id const& other) = default; \ + _Name_##Id& operator=(_Name_##Id const& other) = default; \ + type inline getId() const \ + { \ + return _Getter_##Id(); \ + } \ + \ + type _Getter_##Id() const \ + { \ + return *mColumnIterator; \ + } \ + \ + bool has_##_Getter_() const \ + { \ + return *mColumnIterator >= 0; \ + } \ + \ + template \ + auto _Getter_##_as() const \ + { \ + if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ + o2::soa::notBoundTable(#_Table_); \ + } \ + if (O2_BUILTIN_UNLIKELY(!has_##_Getter_())) { \ + o2::soa::accessingInvalidIndexFor(#_Getter_); \ + } \ + auto t = mBinding.get(); \ + if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ + o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ + } \ + return t->rawIteratorAt(*mColumnIterator); \ + } \ + \ + auto _Getter_() const \ + { \ + return _Getter_##_as(); \ + } \ + \ + template \ + bool setCurrent(T* current) \ + { \ + if constexpr (o2::soa::is_binding_compatible_v()) { \ + assert(current != nullptr); \ + this->mBinding.bind(current); \ + return true; \ + } \ + return false; \ + } \ + \ + bool setCurrentRaw(o2::soa::Binding current) \ + { \ + this->mBinding = current; \ + return true; \ + } \ + binding_t const* getCurrent() const { return mBinding.get(); } \ + o2::soa::Binding getCurrentRaw() const { return mBinding; } \ + o2::soa::Binding mBinding; \ + }; \ + [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_##Id { "fIndex" _Label_ _Suffix_, _Name_##Id::hash, o2::framework::expressions::selectArrowType<_Type_>() } #define DECLARE_SOA_INDEX_COLUMN_FULL(_Name_, _Getter_, _Type_, _Table_, _Suffix_) DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(_Name_, _Getter_, _Type_, _Table_, #_Table_, _Suffix_) #define DECLARE_SOA_INDEX_COLUMN(_Name_, _Getter_) DECLARE_SOA_INDEX_COLUMN_FULL(_Name_, _Getter_, int32_t, _Name_##s, "") #define DECLARE_SOA_INDEX_COLUMN_CUSTOM(_Name_, _Getter_, _Label_) DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(_Name_, _Getter_, int32_t, _Name_##s, _Label_, "") /// SELF -#define DECLARE_SOA_SELF_INDEX_COLUMN_COMPLETE(_Name_, _Getter_, _Type_, _Label_, _IndexTarget_) \ - struct _Name_##Id : o2::soa::Column<_Type_, _Name_##Id> { \ - static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ - static constexpr const char* mLabel = "fIndex" _Label_; \ - using base = o2::soa::Column<_Type_, _Name_##Id>; \ - using type = _Type_; \ - using column_t = _Name_##Id; \ - using self_index_t = std::true_type; \ - using compatible_signature = std::conditional, _IndexTarget_, void>; \ - _Name_##Id(arrow::ChunkedArray const* column) \ - : o2::soa::Column<_Type_, _Name_##Id>(o2::soa::ColumnIterator(column)) \ - { \ - } \ - \ - _Name_##Id() = default; \ - _Name_##Id(_Name_##Id const& other) = default; \ - _Name_##Id& operator=(_Name_##Id const& other) = default; \ - type inline getId() const \ - { \ - return _Getter_##Id(); \ - } \ - \ - type _Getter_##Id() const \ - { \ - return *mColumnIterator; \ - } \ - \ - bool has_##_Getter_() const \ - { \ - return *mColumnIterator >= 0; \ - } \ - \ - template \ - auto _Getter_##_as() const \ - { \ - if (O2_BUILTIN_UNLIKELY(!has_##_Getter_())) { \ - o2::soa::accessingInvalidIndexFor(#_Getter_); \ - } \ - auto t = mBinding.get(); \ - if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ - o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ - } \ - return t->rawIteratorAt(*mColumnIterator); \ - } \ - \ - bool setCurrentRaw(o2::soa::Binding current) \ - { \ - this->mBinding = current; \ - return true; \ - } \ - o2::soa::Binding getCurrentRaw() const { return mBinding; } \ - o2::soa::Binding mBinding; \ - }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_##Id { "fIndex" _Label_, o2::framework::TypeIdHelpers::uniqueId<_Name_##Id>(), \ - o2::framework::expressions::selectArrowType<_Type_>() } +#define DECLARE_SOA_SELF_INDEX_COLUMN_COMPLETE(_Name_, _Getter_, _Type_, _Label_, _IndexTarget_) \ + struct _Name_##Id : o2::soa::Column<_Type_, _Name_##Id> { \ + static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ + static constexpr const char* mLabel = "fIndex" _Label_; \ + static constexpr const uint32_t hash = compile_time_hash(namespace_prefix<_Name_##Id>(), std::string_view{#_Getter_ "Id"}); \ + using base = o2::soa::Column<_Type_, _Name_##Id>; \ + using type = _Type_; \ + using column_t = _Name_##Id; \ + using self_index_t = std::true_type; \ + using compatible_signature = std::conditional, _IndexTarget_, void>; \ + _Name_##Id(arrow::ChunkedArray const* column) \ + : o2::soa::Column<_Type_, _Name_##Id>(o2::soa::ColumnIterator(column)) \ + { \ + } \ + \ + _Name_##Id() = default; \ + _Name_##Id(_Name_##Id const& other) = default; \ + _Name_##Id& operator=(_Name_##Id const& other) = default; \ + type inline getId() const \ + { \ + return _Getter_##Id(); \ + } \ + \ + type _Getter_##Id() const \ + { \ + return *mColumnIterator; \ + } \ + \ + bool has_##_Getter_() const \ + { \ + return *mColumnIterator >= 0; \ + } \ + \ + template \ + auto _Getter_##_as() const \ + { \ + if (O2_BUILTIN_UNLIKELY(!has_##_Getter_())) { \ + o2::soa::accessingInvalidIndexFor(#_Getter_); \ + } \ + auto t = mBinding.get(); \ + if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ + o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ + } \ + return t->rawIteratorAt(*mColumnIterator); \ + } \ + \ + bool setCurrentRaw(o2::soa::Binding current) \ + { \ + this->mBinding = current; \ + return true; \ + } \ + o2::soa::Binding getCurrentRaw() const { return mBinding; } \ + o2::soa::Binding mBinding; \ + }; \ + [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_##Id { "fIndex" _Label_, _Name_##Id::hash, o2::framework::expressions::selectArrowType<_Type_>() } #define DECLARE_SOA_SELF_INDEX_COLUMN_FULL(_Name_, _Getter_, _Type_, _Label_) DECLARE_SOA_SELF_INDEX_COLUMN_COMPLETE(_Name_, _Getter_, _Type_, _Label_, void) #define DECLARE_SOA_SELF_INDEX_COLUMN(_Name_, _Getter_) DECLARE_SOA_SELF_INDEX_COLUMN_FULL(_Name_, _Getter_, int32_t, #_Name_) @@ -2842,6 +2978,7 @@ consteval auto getIndexTargets() struct _Name_##IdSlice : o2::soa::Column<_Type_[2], _Name_##IdSlice> { \ static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ static constexpr const char* mLabel = "fIndexSlice" _Label_; \ + static constexpr const uint32_t hash = 0; \ using base = o2::soa::Column<_Type_[2], _Name_##IdSlice>; \ using type = _Type_[2]; \ using column_t = _Name_##IdSlice; \ @@ -2905,6 +3042,7 @@ consteval auto getIndexTargets() struct _Name_##Ids : o2::soa::Column, _Name_##Ids> { \ static_assert(std::is_integral_v<_Type_>, "Index type must be integral"); \ static constexpr const char* mLabel = "fIndexArray" _Label_; \ + static constexpr const uint32_t hash = 0; \ using base = o2::soa::Column, _Name_##Ids>; \ using type = std::vector<_Type_>; \ using column_t = _Name_##Ids; \ @@ -2940,15 +3078,9 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ } \ - return getIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ auto result = std::vector(); \ for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ + result.push_back(t->rawIteratorAt(i)); \ } \ return result; \ } \ @@ -3021,6 +3153,7 @@ consteval auto getIndexTargets() using callback_holder_t = _Name_##Callback; \ using callable_t = helper::callable_t; \ using callback_t = callable_t::type; \ + static constexpr const uint32_t hash = 0; \ \ _Name_(arrow::ChunkedArray const*) \ { \ @@ -3166,30 +3299,68 @@ consteval auto getIndexTargets() O2HASH(#_Name_ "CfgExtension"); \ DECLARE_SOA_CONFIGURABLE_EXTENDED_TABLE_FULL(_Name_, #_Name_ "CfgExtension", _Table_, "AOD", "EX" _Description_, 0, __VA_ARGS__) -#define DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, _Origin_, _Version_, _Desc_, _Exclusive_, ...) \ - O2HASH(#_Name_); \ - O2HASH(_Desc_ "/" #_Version_); \ - template > \ - struct _Name_##MetadataFrom : o2::aod::TableMetadata, soa::Index<>, __VA_ARGS__> { \ - static constexpr bool exclusive = _Exclusive_; \ - using Key = _Key_; \ - using index_pack_t = framework::pack<__VA_ARGS__>; \ - static constexpr const auto sources = [](framework::pack) { \ - constexpr auto a = o2::soa::mergeOriginals(); \ - return o2::aod::filterForKey(); \ - }(framework::pack<__VA_ARGS__>{}); \ - }; \ - using _Name_##Metadata = _Name_##MetadataFrom>; \ - \ - template > \ - using _Name_##From = o2::soa::IndexTable, o2::aod::Hash<_Desc_ "/" #_Version_ ""_h>, O, _Key_, __VA_ARGS__>; \ - using _Name_ = _Name_##From>; \ - \ - template <> \ - struct MetadataTrait> { \ - using metadata = _Name_##Metadata; \ +#define DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, _Origin_, _Version_, _Desc_, _Exclusive_, ...) \ + O2HASH(#_Name_); \ + O2HASH(_Desc_ "/" #_Version_); \ + template > \ + struct _Name_##MetadataFrom : o2::aod::TableMetadata, soa::Index<>, __VA_ARGS__> { \ + static constexpr bool exclusive = _Exclusive_; \ + using Key = _Key_; \ + using index_pack_t = framework::pack<__VA_ARGS__>; \ + static constexpr const auto sources = [](framework::pack) { \ + constexpr auto a = o2::soa::mergeOriginals(); \ + return o2::aod::filterForKey(); \ + }(framework::pack<__VA_ARGS__>{}); \ + static_assert(sources.size() - Key::originals.size() + 1 == framework::pack_size(index_pack_t{}), "One of the referred tables does not have index to Key"); \ + }; \ + using _Name_##Metadata = _Name_##MetadataFrom>; \ + \ + template > \ + using _Name_##From = o2::soa::IndexTable, o2::aod::Hash<_Desc_ "/" #_Version_ ""_h>, O, _Key_, __VA_ARGS__>; \ + using _Name_ = _Name_##From>; \ + \ + template <> \ + struct MetadataTrait> { \ + using metadata = _Name_##Metadata; \ }; +// Declare were each row is associated to a timestamp column of an _TimestampSource_ +// table. +// +// The columns of this table have to be CCDB_COLUMNS so that for each timestamp, we get a row +// which points to the specified CCDB objectes described by those columns. +#define DECLARE_SOA_TIMESTAMPED_TABLE_FULL(_Name_, _Label_, _TimestampSource_, _TimestampColumn_, _Origin_, _Version_, _Desc_, ...) \ + O2HASH(_Desc_ "/" #_Version_); \ + template \ + using _Name_##TimestampFrom = soa::Table, o2::aod::Hash<_Desc_ "/" #_Version_ ""_h>, O>; \ + using _Name_##Timestamp = _Name_##TimestampFrom>; \ + template > \ + struct _Name_##TimestampMetadataFrom : TableMetadata, __VA_ARGS__> { \ + using base_table_t = _TimestampSource_; \ + using extension_table_t = _Name_##TimestampFrom; \ + static constexpr const auto ccdb_urls = [](framework::pack) { \ + return std::array{Cs::query...}; \ + }(framework::pack<__VA_ARGS__>{}); \ + static constexpr const auto ccdb_bindings = [](framework::pack) { \ + return std::array{Cs::mLabel...}; \ + }(framework::pack<__VA_ARGS__>{}); \ + static constexpr auto sources = _TimestampSource_::originals; \ + static constexpr auto timestamp_column_label = _TimestampColumn_::mLabel; \ + /*static constexpr auto timestampColumn = _TimestampColumn_;*/ \ + }; \ + using _Name_##TimestampMetadata = _Name_##TimestampMetadataFrom>; \ + template <> \ + struct MetadataTrait> { \ + using metadata = _Name_##TimestampMetadata; \ + }; \ + template \ + using _Name_##From = o2::soa::JoinFull, _TimestampSource_, _Name_##TimestampFrom>; \ + using _Name_ = _Name_##From>; + +#define DECLARE_SOA_TIMESTAMPED_TABLE(_Name_, _TimestampSource_, _TimestampColumn_, _Version_, _Desc_, ...) \ + O2HASH(#_Name_ "Timestamped"); \ + DECLARE_SOA_TIMESTAMPED_TABLE_FULL(_Name_, #_Name_ "Timestamped", _TimestampSource_, _TimestampColumn_, "ATIM", _Version_, _Desc_, __VA_ARGS__) + #define DECLARE_SOA_INDEX_TABLE(_Name_, _Key_, _Description_, ...) \ DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, "IDX", 0, _Description_, false, __VA_ARGS__) @@ -3211,12 +3382,16 @@ struct JoinFull : Table, D, o2::aod::Hash<"JOIN"_h>, Ts. JoinFull(std::shared_ptr&& table, uint64_t offset = 0) : base{std::move(table), offset} { - bindInternalIndicesTo(this); + if (this->tableSize() != 0) { + bindInternalIndicesTo(this); + } } JoinFull(std::vector>&& tables, uint64_t offset = 0) : base{ArrowHelpers::joinTables(std::move(tables), std::span{base::originalLabels}), offset} { - bindInternalIndicesTo(this); + if (this->tableSize() != 0) { + bindInternalIndicesTo(this); + } } using base::bindExternalIndices; using base::bindInternalIndicesTo; @@ -3373,7 +3548,7 @@ class FilteredBase : public T mSelectedRowsCache{std::move(selection)}, mCached{true} { - mSelectedRows = gsl::span{mSelectedRowsCache}; + mSelectedRows = std::span{mSelectedRowsCache}; if (this->tableSize() != 0) { mFilteredBegin = table_t::filtered_begin(mSelectedRows); } @@ -3381,7 +3556,7 @@ class FilteredBase : public T mFilteredBegin.bindInternalIndices(this); } - FilteredBase(std::vector>&& tables, gsl::span const& selection, uint64_t offset = 0) + FilteredBase(std::vector>&& tables, std::span const& selection, uint64_t offset = 0) : T{std::move(tables), offset}, mSelectedRows{selection} { @@ -3460,12 +3635,12 @@ class FilteredBase : public T static inline auto getSpan(gandiva::Selection const& sel) { if (sel == nullptr) { - return gsl::span{}; + return std::span{}; } auto array = std::static_pointer_cast(sel->ToArray()); auto start = array->raw_values(); auto stop = start + array->length(); - return gsl::span{start, stop}; + return std::span{start, stop}; } /// Bind the columns which refer to other tables @@ -3564,7 +3739,7 @@ class FilteredBase : public T resetRanges(); } - void sumWithSelection(gsl::span const& selection) + void sumWithSelection(std::span const& selection) { mCached = true; SelectionVector rowsUnion; @@ -3574,7 +3749,7 @@ class FilteredBase : public T resetRanges(); } - void intersectWithSelection(gsl::span const& selection) + void intersectWithSelection(std::span const& selection) { mCached = true; SelectionVector intersection; @@ -3593,7 +3768,7 @@ class FilteredBase : public T void resetRanges() { if (mCached) { - mSelectedRows = gsl::span{mSelectedRowsCache}; + mSelectedRows = std::span{mSelectedRowsCache}; } mFilteredEnd.reset(new RowViewSentinel{static_cast(mSelectedRows.size())}); if (tableSize() == 0) { @@ -3603,7 +3778,7 @@ class FilteredBase : public T } } - gsl::span mSelectedRows; + std::span mSelectedRows; SelectionVector mSelectedRowsCache; bool mCached = false; iterator mFilteredBegin; @@ -3639,7 +3814,7 @@ class Filtered : public FilteredBase Filtered(std::vector>&& tables, SelectionVector&& selection, uint64_t offset = 0) : FilteredBase(std::move(tables), std::forward(selection), offset) {} - Filtered(std::vector>&& tables, gsl::span const& selection, uint64_t offset = 0) + Filtered(std::vector>&& tables, std::span const& selection, uint64_t offset = 0) : FilteredBase(std::move(tables), selection, offset) {} Filtered operator+(SelectionVector const& selection) @@ -3649,7 +3824,7 @@ class Filtered : public FilteredBase return copy; } - Filtered operator+(gsl::span const& selection) + Filtered operator+(std::span const& selection) { Filtered copy(*this); copy.sumWithSelection(selection); @@ -3667,7 +3842,7 @@ class Filtered : public FilteredBase return *this; } - Filtered operator+=(gsl::span const& selection) + Filtered operator+=(std::span const& selection) { this->sumWithSelection(selection); return *this; @@ -3685,7 +3860,7 @@ class Filtered : public FilteredBase return copy; } - Filtered operator*(gsl::span const& selection) + Filtered operator*(std::span const& selection) { Filtered copy(*this); copy.intersectWithSelection(selection); @@ -3703,7 +3878,7 @@ class Filtered : public FilteredBase return *this; } - Filtered operator*=(gsl::span const& selection) + Filtered operator*=(std::span const& selection) { this->intersectWithSelection(selection); return *this; @@ -3811,7 +3986,7 @@ class Filtered> : public FilteredBase } } - Filtered(std::vector>&& tables, gsl::span const& selection, uint64_t offset = 0) + Filtered(std::vector>&& tables, std::span const& selection, uint64_t offset = 0) : FilteredBase(std::move(extractTablesFromFiltered(tables)), selection, offset) { for (auto& table : tables) { @@ -3826,7 +4001,7 @@ class Filtered> : public FilteredBase return copy; } - Filtered> operator+(gsl::span const& selection) + Filtered> operator+(std::span const& selection) { Filtered> copy(*this); copy.sumWithSelection(selection); @@ -3844,7 +4019,7 @@ class Filtered> : public FilteredBase return *this; } - Filtered> operator+=(gsl::span const& selection) + Filtered> operator+=(std::span const& selection) { this->sumWithSelection(selection); return *this; @@ -3862,7 +4037,7 @@ class Filtered> : public FilteredBase return copy; } - Filtered> operator*(gsl::span const& selection) + Filtered> operator*(std::span const& selection) { Filtered> copy(*this); copy.intersectionWithSelection(selection); @@ -3880,7 +4055,7 @@ class Filtered> : public FilteredBase return *this; } - Filtered> operator*=(gsl::span const& selection) + Filtered> operator*=(std::span const& selection) { this->intersectWithSelection(selection); return *this; @@ -3989,7 +4164,7 @@ struct SmallGroupsBase : public Filtered { SmallGroupsBase(std::vector>&& tables, SelectionVector&& selection, uint64_t offset = 0) : Filtered(std::move(tables), std::forward(selection), offset) {} - SmallGroupsBase(std::vector>&& tables, gsl::span const& selection, uint64_t offset = 0) + SmallGroupsBase(std::vector>&& tables, std::span const& selection, uint64_t offset = 0) : Filtered(std::move(tables), selection, offset) {} }; diff --git a/Framework/Core/include/Framework/ASoAHelpers.h b/Framework/Core/include/Framework/ASoAHelpers.h index 5bf474e61f935..0449bdfdc2a0c 100644 --- a/Framework/Core/include/Framework/ASoAHelpers.h +++ b/Framework/Core/include/Framework/ASoAHelpers.h @@ -76,76 +76,13 @@ void dataSizeVariesBetweenColumns(); template