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/reports.yml b/.github/workflows/reports.yml index 936be948b7218..5a04e56382fb3 100644 --- a/.github/workflows/reports.yml +++ b/.github/workflows/reports.yml @@ -22,7 +22,7 @@ jobs: 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/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 e098617cf44e3..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 @@ -37,12 +37,12 @@ api.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a loc 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 +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 */); +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 @@ -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 9668097c39473..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 @@ -112,10 +117,7 @@ class CCDBManagerInstance { // TODO: add some error info/handling when failing mMetaData = metaData; - auto obj = getForTimeStamp(path, timestamp); - if (headers) { - *headers = mHeaders; - } + auto obj = getForTimeStamp(path, timestamp, headers); return obj; } @@ -235,7 +237,7 @@ 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; @@ -258,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++; @@ -323,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; } @@ -374,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 e41f58d5c6da9..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. 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 27ad14cdf24fa..42bc13904bf61 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -40,13 +40,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include "rapidjson/document.h" #include "rapidjson/writer.h" @@ -117,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() @@ -165,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://"; @@ -371,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); @@ -414,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:"; @@ -831,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 @@ -879,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; } 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/testCcdbApi.cxx b/CCDB/test/testCcdbApi.cxx index 0ba037710cf62..1b6a5d6f0967a 100644 --- a/CCDB/test/testCcdbApi.cxx +++ b/CCDB/test/testCcdbApi.cxx @@ -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/CODEOWNERS b/CODEOWNERS index 117ff0d92b272..26021d458ad76 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,7 +34,7 @@ /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 diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index f0198f7a7f61d..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, @@ -93,6 +96,9 @@ enum Pdg { }; /// \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; diff --git a/Common/Constants/include/CommonConstants/make_pdg_header.py b/Common/Constants/include/CommonConstants/make_pdg_header.py index ad24d66e2c3a5..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 @@ -146,7 +152,8 @@ class Pdg(Enum): 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/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/ML/include/ML/OrtInterface.h b/Common/ML/include/ML/OrtInterface.h index 04a5e0ba5c9fc..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(); diff --git a/Common/ML/src/OrtInterface.cxx b/Common/ML/src/OrtInterface.cxx index d30d05d1d1a00..8f88ab18dacbd 100644 --- a/Common/ML/src/OrtInterface.cxx +++ b/Common/ML/src/OrtInterface.cxx @@ -138,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/Framework/Core/include/Framework/DataProcessingStateManager.h b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h similarity index 50% rename from Framework/Core/include/Framework/DataProcessingStateManager.h rename to Common/MathUtils/include/MathUtils/BetheBlochAleph.h index eaa1c8e4e5501..bd72faffb0503 100644 --- a/Framework/Core/include/Framework/DataProcessingStateManager.h +++ b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h @@ -9,27 +9,27 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_DATAPROCESSINGSTATEMANAGER_H_ -#define O2_DATAPROCESSINGSTATEMANAGER_H_ - -#include -#include -#include - -struct DataProcessingStateManager { - struct StateIndex { - short id = -1; - short index = -1; - }; - struct StateInfo { - std::string name; - int64_t lastUpdate = 0; - int index = -1; - }; - - static constexpr int MAX_STATES = 1024; - std::vector> states = {}; - std::vector infos = {}; -}; +#ifndef AliceO2_COMMON_BETHEBLOCH_H_ +#define AliceO2_COMMON_BETHEBLOCH_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::common +{ + +template +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) +{ + T beta = bg / o2::gpu::GPUCommonMath::Sqrt(static_cast(1.) + bg * bg); + + T aa = o2::gpu::GPUCommonMath::Pow(beta, kp4); + T bb = o2::gpu::GPUCommonMath::Pow(static_cast(1.) / bg, kp5); + bb = o2::gpu::GPUCommonMath::Log(kp3 + bb); + + return (kp2 - aa - bb) * kp1 / aa; +} + +} // namespace o2::common #endif diff --git a/Common/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/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/include/CommonUtils/EnumFlags.h b/Common/Utils/include/CommonUtils/EnumFlags.h index 4bd1a9e641056..e7481c903e666 100644 --- a/Common/Utils/include/CommonUtils/EnumFlags.h +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -54,10 +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 { @@ -108,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 { @@ -128,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 @@ -161,7 +163,7 @@ 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_assert(Max_u_v < std::numeric_limits::digits, "Max Bit is beyond allow range defered from underlying type"); + 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) { @@ -258,7 +260,7 @@ struct FlagsHelper final { static constexpr std::optional fromString(std::string_view str) noexcept { for (size_t i{0}; i < count(); ++i) { - if (Names[i] == str || NamesScoped[i] == str) { + if (isIEqual(Names[i], str) || isIEqual(NamesScoped[i], str)) { return Values[i]; } } @@ -277,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()) { @@ -294,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; @@ -306,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; @@ -332,7 +334,7 @@ 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 (upto 64 bits), allowing * manipulation via enum member names. It supports operations akin to std::bitset @@ -355,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}; @@ -388,9 +391,10 @@ class EnumFlags std::for_each(flags.begin(), flags.end(), [this](const E f) noexcept { mBits |= to_bit(f); }); } // Init from a string. - EnumFlags(const std::string& str) + // + explicit EnumFlags(const std::string& str, int base = DefaultBase) { - set(str); + set(str, base); } // Destructor. constexpr ~EnumFlags() = default; @@ -413,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) { @@ -441,39 +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 + 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); @@ -538,8 +545,7 @@ class EnumFlags } // Check if given flag is set. - template - requires std::is_same_v + template T> [[nodiscard]] constexpr bool operator[](const T t) const noexcept { return test(t); @@ -564,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); @@ -573,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); @@ -582,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)); @@ -685,32 +688,89 @@ class EnumFlags // 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."); } } - typename H::UMax 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 == ',' || 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 { - // accept as delimiter ' ', '|', ';', ',' + // Detect delimiter and ensure only one type is used char token = ' '; - std::string::size_type pos = s.find_first_of(",|;"); - if (pos != std::string::npos) { - token = s[pos]; + 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(s, 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/test/testEnumFlags.cxx b/Common/Utils/test/testEnumFlags.cxx index 80f85c847653b..9101ffb97fdfe 100644 --- a/Common/Utils/test/testEnumFlags.cxx +++ b/Common/Utils/test/testEnumFlags.cxx @@ -74,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]); @@ -306,3 +317,337 @@ BOOST_AUTO_TEST_CASE(Flags_test) 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/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/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/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/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h similarity index 63% rename from Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx rename to DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h index 81087744b04a9..9fcd1318914bd 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h @@ -9,14 +9,17 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSReconstruction/CookedConfigParam.h" +#ifndef _FT0_EVENTS_PER_BC_CALIB_OBJECT +#define _FT0_EVENTS_PER_BC_CALIB_OBJECT -namespace o2 -{ -namespace its -{ -static auto& sITSCookedTrackerParam = o2::its::CookedConfigParam::Instance(); +#include "CommonConstants/LHCConstants.h" +#include -O2ParamImpl(o2::its::CookedConfigParam); -} // namespace its -} // namespace o2 +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/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/TOF/CMakeLists.txt b/DataFormats/Detectors/TOF/CMakeLists.txt index 8a55e531287e1..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 @@ -22,6 +30,7 @@ o2_add_library(DataFormatsTOF src/TOFFEElightInfo.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats O2::GPUCommon + O2::DataFormatsParamTOF Boost::serialization) o2_target_root_dictionary(DataFormatsTOF @@ -33,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 2f15923419795..37d3ca23ddb35 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -49,9 +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 - // - FrameBit = 6 }; // this bit set means that the cluster is in the nominal (alpha=20*sector+10 deg.) sector frame rather than aligned + kLeft = 7 // 2^7, 8th bit + }; Cluster() = default; @@ -59,8 +58,8 @@ class Cluster : public o2::BaseCluster ~Cluster() = default; - bool isInNominalSector() const { return isBitSet(FrameBit); } - void setInNominalSector() { setBit(FrameBit); } + 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); } @@ -163,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 c9d910d8345e5..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 { @@ -37,7 +37,7 @@ class Parameters Parameters(std::array parNames, std::string name) : mName{name}, mPar{}, mParNames{parNames} {}; /// Default destructor - virtual ~Parameters() = default; // Ensure proper cleanup in derived classes + ~Parameters() = default; /// Setter for the parameter at position iparam /// \param iparam index in the array of the parameters @@ -183,27 +183,10 @@ class ParameterCollection : public TNamed /// @param value parameter to add to the stored information /// @param pass key to look for in the stored information e.g. pass /// @return true if found and configured false if not fully configured - bool addParameter(const std::string& pass, const std::string& parName, float value) - { - const bool alreadyPresent = hasKey(pass); - if (alreadyPresent) { - LOG(debug) << "Changing parametrization corresponding to key " << pass << " from size " << mParameters[pass].size() << " to " << parName; - } else { - mParameters[pass] = std::unordered_map{}; - LOG(debug) << "Adding new parametrization corresponding to key " << pass << ": " << parName; - } - mParameters[pass][parName] = value; - return true; - } + bool addParameter(const std::string& pass, const std::string& parName, float value); /// @return the size of the container i.e. the number of stored keys (or passes) - int getSize(const std::string& pass) const - { - if (!hasKey(pass)) { - return -1; - } - return mParameters.at(pass).size(); - } + int getSize(const std::string& pass) const; /// @brief Function to push the parameters from the sub container into the collection and store it under a given key /// @tparam ParType type of the parameter container @@ -227,34 +210,24 @@ 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); } - - /// @brief printing function for the content of the pass - /// @param pass pass to print - void print(const std::string& pass) const + const std::unordered_map& getPars(const std::string& pass) const { - const auto& size = getSize(pass); - if (size < 0) { - LOG(info) << "empty pass: " << pass; - return; - } - LOG(info) << "Pass \"" << pass << "\" with size " << size; - for (const auto& [par, value] : mParameters.at(pass)) { - LOG(info) << "par name = " << par << ", value = " << value; + 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 + void print(const std::string& pass) const; + /// @brief printing function for the full content of the container - void print() const - { - for (const auto& [pass, pars] : mParameters) { - print(pass); - } - } + void print() const; /// @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/TOF/src/ParameterContainers.cxx b/DataFormats/Detectors/TOF/src/ParameterContainers.cxx new file mode 100644 index 0000000000000..91f723873e9cd --- /dev/null +++ b/DataFormats/Detectors/TOF/src/ParameterContainers.cxx @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ParameterContainers.h +/// \author Francesco Noferini +/// \author Nicolò Jacazio nicolo.jacazio@cern.ch +/// @since 2022-11-08 +/// \brief Implementation of the containers for the general parameters + +#include "DataFormatsTOF/ParameterContainers.h" + +// ClassImp(o2::tof::Parameters); +using namespace o2::tof; + +bool ParameterCollection::addParameter(const std::string& pass, const std::string& parName, float value) +{ + const bool alreadyPresent = hasKey(pass); + if (alreadyPresent) { + LOG(debug) << "Changing parametrization corresponding to key " << pass << " from size " << mParameters[pass].size() << " to " << parName; + } else { + mParameters[pass] = std::unordered_map{}; + LOG(debug) << "Adding new parametrization corresponding to key " << pass << ": " << parName; + } + mParameters[pass][parName] = value; + return true; +} + +int ParameterCollection::getSize(const std::string& pass) const +{ + if (!hasKey(pass)) { + return -1; + } + return mParameters.at(pass).size(); +} + +void ParameterCollection::print() const +{ + for (const auto& [pass, pars] : mParameters) { + print(pass); + } +} + +void ParameterCollection::print(const std::string& pass) const +{ + const auto& size = getSize(pass); + if (size < 0) { + LOG(info) << "empty pass: " << pass; + return; + } + LOG(info) << "Pass \"" << pass << "\" with size " << size; + for (const auto& [par, value] : mParameters.at(pass)) { + LOG(info) << "par name = " << par << ", value = " << value; + } +} diff --git a/DataFormats/Detectors/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index 5aed01a9772c1..2cc69e16001a6 100644 --- a/DataFormats/Detectors/TPC/CMakeLists.txt +++ b/DataFormats/Detectors/TPC/CMakeLists.txt @@ -40,7 +40,6 @@ o2_add_library( o2_target_root_dictionary( DataFormatsTPC - EXTRA_PATCH src/VectorPadflagsCustomStreamer.cxx HEADERS include/DataFormatsTPC/ClusterGroupAttribute.h include/DataFormatsTPC/ClusterNative.h include/DataFormatsTPC/ClusterNativeHelper.h 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/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/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index 8659e6a2e43eb..fd5abca99cb0f 100644 --- a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h +++ b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h @@ -22,7 +22,6 @@ #pragma link C++ class o2::tpc::ClusterHardwareContainerFixedSize < 8192> + ; #pragma link C++ class o2::tpc::ClusterNativeContainer + ; #pragma link C++ class o2::tpc::Digit + ; -// pragma link C++ enum o2::tpc::PadFlags +; // enum itself #pragma link C++ class o2::tpc::ZeroSuppressedContainer8kb + ; #pragma link C++ class std::vector < o2::tpc::ClusterNative> + ; #pragma link C++ class std::vector < o2::tpc::ClusterNativeContainer> + ; @@ -30,7 +29,6 @@ #pragma link C++ class std::vector < o2::tpc::ClusterHardwareContainerFixedSize < 8192>> + ; #pragma link C++ class std::vector < o2::tpc::ClusterHardwareContainer8kb> + ; #pragma link C++ class std::vector < o2::tpc::Digit> + ; -// pragma link C++ class std::vector < o2::tpc::PadFlags> + ; #pragma link C++ class std::vector < o2::tpc::ZeroSuppressedContainer8kb> + ; #pragma link C++ class o2::tpc::TrackTPC + ; #pragma link C++ class o2::tpc::LaserTrack + ; diff --git a/DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx b/DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx deleted file mode 100644 index f7cb9285b8884..0000000000000 --- a/DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -using std::vector; - -void VectorPadFlagsStreamer(TBuffer& R__b, void* objp) -{ - std::vector* obj = static_cast*>(objp); - if (R__b.IsReading()) { - 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()); - for (int i = 0; i < 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); - } - } -} - -#define RootStreamerLocal(name, STREAMER) \ - namespace ROOT \ - { \ - \ - /** \cond HIDDEN_SYMBOLS */ \ - static auto _R__UNIQUE_(R__dummyStreamer) = \ - []() { TClass::GetClass()->SetStreamerFunc(STREAMER); return 0; }(); \ - /** \endcond */ \ - R__UseDummy(_R__UNIQUE_(R__dummyStreamer)); \ - } - -// Let's not try to fix the old ROOT version, so that we can build -// the new ROOT with the patched code in the CI. -#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 33, 00) -RootStreamerLocal(vector, VectorPadFlagsStreamer); -#endif 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/FDD/workflow/src/RawDataReaderSpec.cxx b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h similarity index 72% rename from Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx rename to DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h index 631655d3038ec..1014b3d8c704e 100644 --- a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.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 RawDataReaderSpec.cxx +#ifdef __CLING__ -#include "FDDWorkflow/RawDataReaderSpec.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 fdd -{ - -} // namespace fdd -} // 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/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/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 bd2cb0c5cbb27..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.). @@ -39,8 +40,9 @@ struct AggregatedRunInfo { // we may have pointers to actual data source objects GRPECS, ... const o2::parameters::GRPECSObject* grpECS = nullptr; // pointer to GRPECSobject (fetched during struct building) + const o2::parameters::GRPLHCIFData* grpLHC = nullptr; - static AggregatedRunInfo buildAggregatedRunInfo(int runnumber, long sorMS, long eorMS, long orbitResetMUS, const o2::parameters::GRPECSObject* grpecs, const std::vector* ctfFirstRunOrbitVec); + 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); diff --git a/DataFormats/Parameters/src/AggregatedRunInfo.cxx b/DataFormats/Parameters/src/AggregatedRunInfo.cxx index 5495ae73bd6ca..40402a33af68b 100644 --- a/DataFormats/Parameters/src/AggregatedRunInfo.cxx +++ b/DataFormats/Parameters/src/AggregatedRunInfo.cxx @@ -15,6 +15,7 @@ #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 @@ -42,14 +43,15 @@ o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo_DATA 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,7 +83,7 @@ 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 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 ffd88df2412f9..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 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h index 87cd059e0b325..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(); 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 2d13e029f8c00..cb1c9d5d87c7f 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h @@ -45,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; @@ -58,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]; } @@ -69,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; } @@ -105,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); } @@ -133,10 +144,8 @@ 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; diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index 0539278acb20b..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; 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/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/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/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/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index dc3c560a1485b..79e36aa9fa48b 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -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) { 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/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 615a7f96de13e..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.}; 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 b8d295a4393e4..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) { @@ -1063,7 +1063,8 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& source == 0, // background mMcParticleW, mMcParticleMom, - mMcParticlePos); + mMcParticlePos, + mUseSigFiltMC); mcReader.releaseTracksForSourceAndEvent(source, event); } @@ -1718,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"); @@ -2126,9 +2129,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) 0, sourceID); } - if (sourceID != 0 || !mUseSigFiltMC) { - mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision - } + mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision } } } @@ -2646,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; @@ -3349,6 +3356,8 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo 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"}}, diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 559eabc3f018b..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" 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 30ab4c4fe8a40..83a9193274e4f 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -29,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 @@ -46,6 +48,7 @@ o2_add_library(DetectorsBase O2::GPUDataTypes MC::VMC TBB::tbb + ROOT::Gdml ) o2_target_root_dictionary(DetectorsBase @@ -62,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) 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/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index 0a53ab00b16f2..cba6e5cebcfc8 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -87,7 +87,7 @@ class MatLayerCylSet : public o2::gpu::FlatObject void flatten(); MatLayerCyl& getLayer(int i) { return get()->mLayers[i]; } - MatLayerCylSet* extractCopy(float rmin, float rmax, float tol = 1e-3) const; + MatLayerCylSet* extractCopy(float rmin, float rmax, float tol = 1e-3, const MatLayerCylSet* toAdd = nullptr) const; void finalizeStructures(); #endif // !GPUCA_ALIGPUCODE 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 d9b1522f4295b..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; @@ -95,10 +103,33 @@ class PropagatorImpl 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; diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h similarity index 51% rename from Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h rename to Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h index a4988b2c18fc7..5ec85f1c14702 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.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_FIT_FT0WORKFLOW_H -#define O2_FIT_FT0WORKFLOW_H +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo -/// @file FT0Workflow.h +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ -#include "Framework/WorkflowSpec.h" +class TGeoShape; +class TGeoTessellated; namespace o2 { -namespace ft0 +namespace base { -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist); -} // namespace ft0 + +/// 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/MatLayerCylSet.cxx b/Detectors/Base/src/MatLayerCylSet.cxx index c287723839d22..1d904ed01e699 100644 --- a/Detectors/Base/src/MatLayerCylSet.cxx +++ b/Detectors/Base/src/MatLayerCylSet.cxx @@ -608,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)) { @@ -618,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 {} +#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 b6112cd5ba32e..a5983cab8e257 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -218,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, @@ -295,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 @@ -352,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, @@ -514,6 +648,23 @@ GPUd() bool PropagatorImpl::propagateToR(track_T& track, value_type r, 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 @@ -564,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); @@ -584,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; @@ -613,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); @@ -633,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; @@ -662,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); @@ -683,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); @@ -731,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; 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/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/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 3810230637e5f..4100ebb37c61d 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -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/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/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/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/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/ctpCCDBManager.cxx b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx index df75b21c2effd..74d4a905c93e2 100644 --- a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx +++ b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx @@ -204,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); 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; @@ -245,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/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 c194f570e47d1..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) { 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/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/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/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/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/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/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 7e3f8d9f78fc9..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 @@ -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/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index 043fe0e659ba6..ea566f15a0b59 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -255,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/study/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt index ab900c7efd87b..df42af503db46 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -9,7 +9,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -add_compile_options(-O0 -g -fPIC) +#add_compile_options(-O0 -g -fPIC) o2_add_library(GlobalTrackingStudy TARGETVARNAME targetName @@ -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/TrackMCStudyConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h index 2b92c00a989cf..ed78ba2a710ec 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h @@ -28,11 +28,13 @@ struct TrackMCStudyConfig : o2::conf::ConfigurableParamHelper #include @@ -289,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{}; 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/Foundation/src/Traits.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx similarity index 84% rename from Framework/Foundation/src/Traits.cxx rename to Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx index faff430964e73..a754d1196017f 100644 --- a/Framework/Foundation/src/Traits.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 09ef766aa1536..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) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index 9dba400fe6edc..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) @@ -99,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); @@ -122,6 +126,9 @@ 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 @@ -221,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()); @@ -570,11 +577,30 @@ 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]) { @@ -592,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) { @@ -714,6 +758,10 @@ void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) }); (*mDBGOut) << "mcVtxTree" << "mcVtx=" << mcVtx << "\n"; } + + if (params.storeITSInfo) { + processITSTracks(recoData); + } } void TrackMCStudy::processTPCTrackRefs() @@ -986,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; + } } //_____________________________________________________ @@ -1239,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/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index f1a07c10d0318..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) @@ -444,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 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/TPCResidualAggregatorSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h index b9c99f9e65676..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 @@ -151,7 +152,7 @@ class ResidualAggregatorDevice : public o2::framework::Task 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(), @@ -222,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 da2fcaab913d7..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,11 +109,6 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) mInterpolation.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); } - if (mDebugOutput) { - mInterpolation.setDumpTrackPoints(); - } - mInterpolation.setExtDetResid(mExtDetResid); - mInterpolation.setITSClusterDictionary(mITSDict); } void TPCInterpolationDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -143,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()); @@ -188,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); 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/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 1fc4442e3bdbf..90ed033ed67da 100644 --- a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx +++ b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx @@ -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/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/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/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/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index 2bd1550e7c72b..d6d87eb8c1143 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -35,6 +35,8 @@ class TimeFrameGPU final : public TimeFrame ~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, IndexTableUtilsN* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); @@ -78,8 +80,8 @@ class TimeFrameGPU final : public TimeFrame void createNeighboursIndexTablesDevice(const int); void createNeighboursDevice(const unsigned int layer); void createNeighboursLUTDevice(const int, const unsigned int); - 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(); @@ -138,6 +140,8 @@ class TimeFrameGPU final : public TimeFrame int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } CellSeedN** getDeviceArrayCells() { return mCellsDeviceArray; } CellSeedN* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + int* getDeviceTrackSeedsLUT() { return mTrackSeedsLUTDevice; } + auto getNTrackSeeds() const { return mNTracks; } o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } @@ -177,8 +181,8 @@ class TimeFrameGPU final : public TimeFrame int getNumberOfNeighbours() const final; private: - void allocMemAsync(void**, size_t, Stream&, bool); // Abstract owned and unowned memory allocations on specific stream - void allocMem(void**, size_t, bool); // Abstract owned and unowned memory allocations on default stream + 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 @@ -217,6 +221,8 @@ class TimeFrameGPU final : public TimeFrame 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; diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 69d6799686654..53992ccf3eb85 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -207,18 +207,48 @@ void processNeighboursHandler(const int startLayer, const int nThreads); template -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); +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 e5b9253ca4090..ee0a203f32fda 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -313,7 +313,7 @@ struct TypedAllocator { pointer allocate(size_type n) { - void* raw = mInternalAllocator->allocate(n * sizeof(T)); + void* raw = mInternalAllocator->allocateStack(n * sizeof(T)); return thrust::device_pointer_cast(static_cast(raw)); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index 1f6a046a81350..e38dbb1ef20e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -35,5 +35,5 @@ if(CUDA_ENABLED) set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) target_compile_definitions(${targetName} PRIVATE $) - set_target_cuda_arch(${targetName}) + 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 27bcf04746da5..da0cd51478945 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -24,15 +24,16 @@ #include "GPUCommonMath.h" #include "GPUCommonLogger.h" #include "GPUCommonHelpers.h" +#include "utils/strtag.h" namespace o2::its::gpu { template -void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator) +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) { if (extAllocator) { - *ptr = this->mExtDeviceAllocator->allocate(size); + *ptr = (this->mExternalAllocator)->allocate(size, type); } else { GPULog("Calling default CUDA allocator"); GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, stream.get())); @@ -40,10 +41,10 @@ void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& strea } template -void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator) +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) { if (extAllocator) { - *ptr = this->mExtDeviceAllocator->allocate(size); + *ptr = (this->mExternalAllocator)->allocate(size, type); } else { GPULog("Calling default CUDA allocator"); GPUChkErrS(cudaMalloc(reinterpret_cast(ptr), size)); @@ -56,7 +57,7 @@ void TimeFrameGPU::loadIndexTableUtils(const int iteration) GPUTimer timer("loading indextable utils"); if (!iteration) { GPULog("gpu-allocation: allocating IndexTableUtils buffer, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); - allocMem(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtilsN), this->hasExternalDeviceAllocator()); + 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)); @@ -67,10 +68,10 @@ void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteratio { if (!iteration) { GPUTimer timer("creating unsorted clusters array"); - allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); mPinnedUnsortedClusters.set(nLayers); - if (!this->hasExternalDeviceAllocator()) { + 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); @@ -85,7 +86,7 @@ void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, cons 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->hasExternalDeviceAllocator()); + 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())); } @@ -96,10 +97,10 @@ void TimeFrameGPU::createClustersDeviceArray(const int iteration, const { if (!iteration) { GPUTimer timer("creating sorted clusters array"); - allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); mPinnedClusters.set(nLayers); - if (!this->hasExternalDeviceAllocator()) { + 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); @@ -114,7 +115,7 @@ void TimeFrameGPU::loadClustersDevice(const int iteration, const int la 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->hasExternalDeviceAllocator()); + 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())); } @@ -125,10 +126,10 @@ void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) { if (!iteration) { GPUTimer timer("creating clustersindextable array"); - allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); mPinnedClustersIndexTables.set(nLayers); - if (!this->hasExternalDeviceAllocator()) { + 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); @@ -143,7 +144,7 @@ void TimeFrameGPU::loadClustersIndexTables(const int iteration, const i if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); GPULog("gpu-transfer: loading clusters indextable for layer {} with {} elements, for {:.2f} MB.", layer, this->mIndexTables[layer].size(), this->mIndexTables[layer].size() * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[layer]), this->mIndexTables[layer].size() * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); + 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())); } @@ -154,10 +155,10 @@ void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, c { if (!iteration) { GPUTimer timer("creating used clusters flags"); - allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); mPinnedUsedClusters.set(nLayers); - if (!this->hasExternalDeviceAllocator()) { + 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); @@ -172,7 +173,7 @@ void TimeFrameGPU::createUsedClustersDevice(const int iteration, const if (!iteration) { GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); GPULog("gpu-transfer: creating {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[layer].size(), layer, this->mUsedClusters[layer].size() * sizeof(unsigned char) / constants::MB); - allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->hasExternalDeviceAllocator()); + 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())); } @@ -193,10 +194,10 @@ void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration { if (!iteration) { GPUTimer timer("creating ROFrame clusters array"); - allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); mPinnedROFramesClusters.set(nLayers); - if (!this->hasExternalDeviceAllocator()) { + 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); @@ -211,7 +212,7 @@ void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const 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->hasExternalDeviceAllocator()); + 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())); } @@ -222,10 +223,10 @@ void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iterati { if (!iteration) { GPUTimer timer("creating trackingframeinfo array"); - allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); mPinnedTrackingFrameInfo.set(nLayers); - if (!this->hasExternalDeviceAllocator()) { + 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); @@ -240,7 +241,7 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, con if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); GPULog("gpu-transfer: loading {} tfinfo on layer {}, for {:.2f} MB.", this->mTrackingFrameInfo[layer].size(), layer, this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[layer]), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), mGpuStreams[layer], this->hasExternalDeviceAllocator()); + 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())); } @@ -253,7 +254,7 @@ void TimeFrameGPU::loadMultiplicityCutMask(const int 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->hasExternalDeviceAllocator()); + 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)); } @@ -265,10 +266,10 @@ void TimeFrameGPU::loadVertices(const int iteration) if (!iteration) { GPUTimer timer("loading seeding vertices"); GPULog("gpu-transfer: loading {} ROframes vertices, for {:.2f} MB.", this->mROFramesPV.size(), this->mROFramesPV.size() * sizeof(int) / constants::MB); - allocMem(reinterpret_cast(&mROFramesPVDevice), this->mROFramesPV.size() * sizeof(int), this->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice)); } } @@ -277,7 +278,7 @@ template void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { if (!iteration) { - allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); } } @@ -288,7 +289,7 @@ void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const const int ncls = this->mClusters[layer].size() + 1; if (!iteration) { GPULog("gpu-allocation: creating tracklets LUT for {} elements on layer {}, for {:.2f} MB.", ncls, layer, ncls * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); + allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpyAsync(&mTrackletsLUTDeviceArray[layer], &mTrackletsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); @@ -299,7 +300,7 @@ void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating tracklet buffers array"); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); } } @@ -311,7 +312,7 @@ void TimeFrameGPU::createTrackletsBuffers(const int layer) 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->hasExternalDeviceAllocator()); + 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())); } @@ -343,7 +344,7 @@ void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells neighbours", layer); GPULog("gpu-transfer: reserving neighbours LUT for {} elements on layer {}, for {:.2f} MB.", mNCells[layer] + 1, layer, (mNCells[layer] + 1) * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[layer]), (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); + 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())); } @@ -352,7 +353,7 @@ void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const uns { 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->hasExternalDeviceAllocator()); // We need one element more to move exc -> inc + 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())); } @@ -362,8 +363,8 @@ void TimeFrameGPU::loadCellsDevice() GPUTimer timer(mGpuStreams, "loading cell seeds", nLayers - 2); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { GPULog("gpu-transfer: loading {} cell seeds on layer {}, for {:.2f} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeedN) / constants::MB); - allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeedN), mGpuStreams[iLayer], this->hasExternalDeviceAllocator()); - allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->hasExternalDeviceAllocator()); // accessory for the neigh. finding. + 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())); } @@ -374,7 +375,7 @@ void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells LUTs array"); - allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); } } @@ -383,7 +384,7 @@ 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->hasExternalDeviceAllocator()); + 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())); } @@ -393,7 +394,7 @@ void TimeFrameGPU::createCellsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells buffers array"); - allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeedN*), cudaMemcpyHostToDevice)); } } @@ -406,7 +407,7 @@ void TimeFrameGPU::createCellsBuffers(const int layer) 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->hasExternalDeviceAllocator()); + 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())); } @@ -426,7 +427,7 @@ void TimeFrameGPU::loadRoadsDevice() { 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->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mRoadsDevice), this->mRoads.size() * sizeof(Road), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpy(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice)); } @@ -436,9 +437,11 @@ void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seed { 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->hasExternalDeviceAllocator()); - GPUChkErrS(cudaHostRegister(seeds.data(), seeds.size() * sizeof(CellSeedN), cudaHostRegisterPortable)); + 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 @@ -449,21 +452,22 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + 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::createTrackITSExtDevice(bounded_vector& seeds) +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) { GPUTimer timer("reserving tracks"); - mTrackITSExt = bounded_vector(seeds.size(), {}, this->getMemoryPool().get()); - GPULog("gpu-allocation: reserving {} tracks, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(o2::its::TrackITSExt) / constants::MB); - allocMem(reinterpret_cast(&mTrackITSExtDevice), seeds.size() * sizeof(o2::its::TrackITSExt), this->hasExternalDeviceAllocator()); - GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, seeds.size() * sizeof(o2::its::TrackITSExt))); - GPUChkErrS(cudaHostRegister(mTrackITSExt.data(), seeds.size() * sizeof(o2::its::TrackITSExt), cudaHostRegisterPortable)); + 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 @@ -474,13 +478,13 @@ void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + 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())); @@ -489,13 +493,13 @@ void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) mGpuStreams[0].sync(); mGpuStreams[1].sync(); if (!iteration) { - allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); } } @@ -508,11 +512,11 @@ void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + 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)); } @@ -524,14 +528,14 @@ void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + 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->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mUsedTrackletsDevice), ntrkls * sizeof(uint8_t), this->hasFrameworkAllocator()); } template @@ -542,7 +546,7 @@ void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) 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->hasExternalDeviceAllocator()); + allocMem(reinterpret_cast(&mLinesDevice), nlines * sizeof(Line), this->hasFrameworkAllocator()); // reset used tracklets GPUChkErrS(cudaMemset(mUsedTrackletsDevice, 0, this->mTotalTracklets[0] * sizeof(uint8_t))); } @@ -586,13 +590,11 @@ void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut } template -void TimeFrameGPU::downloadTrackITSExtDevice(bounded_vector& seeds) +void TimeFrameGPU::downloadTrackITSExtDevice() { GPUTimer timer("downloading tracks"); GPULog("gpu-transfer: downloading {} tracks, for {:.2f} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / constants::MB); - GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, seeds.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); - GPUChkErrS(cudaHostUnregister(mTrackITSExt.data())); - GPUChkErrS(cudaHostUnregister(seeds.data())); + GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); } template @@ -630,6 +632,39 @@ void TimeFrameGPU::unregisterHostMemory(const int maxLayers) 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::pushMemoryStack(const int iteration) +{ + // mark the beginning of memory marked with MEMORY_STACK that can be discarded + // after doing one iteration + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); +} + +template +void TimeFrameGPU::popMemoryStack(const int iteration) +{ + // pop all memory on the stack from this iteration + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); +} + template void TimeFrameGPU::initialise(const int iteration, const TrackingParameters& trkParam, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index cca4283c9b77f..42d2227de60f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -28,7 +28,6 @@ template void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) { mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], nLayers); - // on default stream mTimeFrameGPU->loadVertices(iteration); mTimeFrameGPU->loadIndexTableUtils(iteration); @@ -45,6 +44,8 @@ void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) mTimeFrameGPU->createTrackletsBuffersArray(iteration); mTimeFrameGPU->createCellsBuffersArray(iteration); mTimeFrameGPU->createCellsLUTDeviceArray(iteration); + // push every create artefact on the stack + mTimeFrameGPU->pushMemoryStack(iteration); } template @@ -108,7 +109,7 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), - mTimeFrameGPU->getExternalDeviceAllocator(), + mTimeFrameGPU->getFrameworkAllocator(), conf.nBlocksLayerTracklets[iteration], conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); @@ -146,7 +147,7 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), - mTimeFrameGPU->getExternalDeviceAllocator(), + mTimeFrameGPU->getFrameworkAllocator(), conf.nBlocksLayerTracklets[iteration], conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); @@ -197,7 +198,7 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, - mTimeFrameGPU->getExternalDeviceAllocator(), + mTimeFrameGPU->getFrameworkAllocator(), conf.nBlocksLayerCells[iteration], conf.nThreadsLayerCells[iteration], mTimeFrameGPU->getStreams()); @@ -253,7 +254,7 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) currentLayerCellsNum, nextLayerCellsNum, 1e2, - mTimeFrameGPU->getExternalDeviceAllocator(), + mTimeFrameGPU->getFrameworkAllocator(), conf.nBlocksFindNeighbours[iteration], conf.nThreadsFindNeighbours[iteration], mTimeFrameGPU->getStream(iLayer)); @@ -281,7 +282,7 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) mTimeFrameGPU->getDeviceNeighbours(iLayer), mTimeFrameGPU->getArrayNNeighbours()[iLayer], mTimeFrameGPU->getStream(iLayer), - mTimeFrameGPU->getExternalDeviceAllocator()); + mTimeFrameGPU->getFrameworkAllocator()); } mTimeFrameGPU->syncStreams(false); } @@ -312,7 +313,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].MaxChi2NDF, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getExternalDeviceAllocator(), + mTimeFrameGPU->getFrameworkAllocator(), conf.nBlocksProcessNeighbours[iteration], conf.nThreadsProcessNeighbours[iteration]); } @@ -321,24 +322,52 @@ void TrackerTraitsGPU::findRoads(const int iteration) 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->mTrkParams[0].CorrType, // o2::base::PropagatorImpl::MatCorrType - conf.nBlocksTracksSeeds[iteration], - conf.nThreadsTracksSeeds[iteration]); - - 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(); @@ -386,6 +415,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) } mTimeFrameGPU->loadUsedClustersDevice(); } + // 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 2c43ccd3bb81c..eacf514c7a91d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -32,9 +32,9 @@ #include "ITStracking/Cluster.h" #include "ITStracking/Cell.h" #include "DataFormatsITS/TrackITS.h" - #include "ITStrackingGPU/TrackingKernels.h" #include "ITStrackingGPU/Utils.h" +#include "utils/strtag.h" // O2 track model #include "ReconstructionDataFormats/Track.h" @@ -57,35 +57,56 @@ GPUdii() bool fitTrack(TrackITSExt& track, float bz, const TrackingFrameInfo** tfInfos, const o2::base::Propagator* prop, - o2::base::PropagatorF::MatCorrType matCorrType) + 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 - if (!track.correctForMaterial(xx0, xx0 * constants::Radl * constants::Rho, 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; } @@ -93,6 +114,10 @@ GPUdii() 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); @@ -101,35 +126,85 @@ GPUdii() bool fitTrack(TrackITSExt& track, GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3, - const float bz) + 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 { @@ -202,31 +277,36 @@ struct compare_track_chi2 { } }; -template +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); - auto& clusters = seed.getClusters(); - for (int iL{0}; iL < nLayers; ++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, @@ -238,14 +318,17 @@ GPUg() void __launch_bounds__(256, 1) 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, @@ -257,11 +340,46 @@ GPUg() void __launch_bounds__(256, 1) 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; + } } } @@ -281,6 +399,11 @@ GPUg() void __launch_bounds__(256, 1) 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]}; @@ -346,8 +469,13 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( 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]}; @@ -387,7 +515,7 @@ GPUg() void __launch_bounds__(256, 1) 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; } @@ -408,11 +536,11 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( 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; + } } } @@ -574,8 +702,13 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( 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) { @@ -644,23 +777,6 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( } } -GPUhi() void allocateMemory(void** p, size_t bytes, cudaStream_t stream = nullptr, ExternalAllocator* alloc = nullptr) -{ - if (alloc) { - *p = alloc->allocate(bytes); - } else { - GPUChkErrS(cudaMallocAsync(p, bytes, stream)); - } -} - -GPUhi() void deallocateMemory(void* p, size_t bytes, cudaStream_t stream = nullptr, ExternalAllocator* alloc = nullptr) -{ - if (alloc) { - alloc->deallocate(reinterpret_cast(p), bytes); - } else { - GPUChkErrS(cudaFreeAsync(p, stream)); - } -} } // namespace gpu template @@ -990,6 +1106,8 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads) { + constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); + alloc->pushTagOnStack(Tag); auto allocInt = gpu::TypedAllocator(alloc); auto allocCellSeed = gpu::TypedAllocator>(alloc); thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); @@ -1100,39 +1218,103 @@ void processNeighboursHandler(const int startLayer, auto s{end - outSeeds.begin()}; seedsHost.reserve(seedsHost.size() + s); thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); + alloc->popTagOffStack(Tag); } template -void 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) +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_vector layerRadii(layerRadiiHost); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + nullptr, // TrackITSExt* + seedLUT, // int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::exclusive_scan(sync_policy, seedLUT, seedLUT + nSeeds + 1, seedLUT); +} + +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) +{ + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + tracks, // TrackITSExt* + seedLUT, // const int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); thrust::device_ptr tr_ptr(tracks); - thrust::sort(tr_ptr, tr_ptr + nSeeds, gpu::compare_track_chi2()); - GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + thrust::sort(sync_policy, tr_ptr, tr_ptr + nTracks, gpu::compare_track_chi2()); } /// Explicit instantiation of ITS2 handlers @@ -1296,18 +1478,46 @@ template void processNeighboursHandler<7>(const int startLayer, const int nBlocks, const int nThreads); -template void trackSeedHandler(CellSeed<7>* 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<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/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index e8e475f2232c8..a40aac491a386 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -13,6 +13,8 @@ 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 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index d7c4e27add739..10e1681c73e8d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -66,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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h index 36e78ef24020c..7d1e98736db2c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h @@ -17,15 +17,39 @@ #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 @@ -36,7 +60,7 @@ class ExternalAllocatorAdaptor final : public std::pmr::memory_resource protected: void* do_allocate(size_t bytes, size_t alignment) override { - void* p = mAlloc->allocate(bytes); + void* p = mAlloc->allocate(bytes, o2::gpu::GPUMemoryResource::MemoryType::MEMORY_HOST); if (!p) { throw std::bad_alloc(); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index b324092624a6d..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" @@ -235,23 +234,14 @@ struct TimeFrame { void setBz(float bz) { mBz = bz; } float getBz() const { return mBz; } - /// State if memory will be externally managed. - // device - ExternalAllocator* mExtDeviceAllocator{nullptr}; - void setExternalDeviceAllocator(ExternalAllocator* allocator) { mExtDeviceAllocator = allocator; } - ExternalAllocator* getExternalDeviceAllocator() { return mExtDeviceAllocator; } - bool hasExternalDeviceAllocator() const noexcept { return mExtDeviceAllocator != nullptr; } - // host - ExternalAllocator* mExtHostAllocator{nullptr}; - void setExternalHostAllocator(ExternalAllocator* allocator) - { - mExtHostAllocator = allocator; - mExtMemoryPool = std::make_shared(mExtHostAllocator); - } - ExternalAllocator* getExternalHostAllocator() { return mExtHostAllocator; } - bool hasExternalHostAllocator() const noexcept { return mExtHostAllocator != nullptr; } - std::shared_ptr mExtMemoryPool; - std::pmr::memory_resource* getMaybeExternalHostResource(bool forceHost = false) { return (hasExternalHostAllocator() && !forceHost) ? mExtMemoryPool.get() : mMemoryPool.get(); } + /// State if memory will be externally managed by the GPU framework + ExternalAllocator* mExternalAllocator{nullptr}; + std::shared_ptr mExtMemoryPool; // host memory pool managed by the framework + auto getFrameworkAllocator() { return mExternalAllocator; }; + void setFrameworkAllocator(ExternalAllocator* ext); + bool hasFrameworkAllocator() const noexcept { return mExternalAllocator != nullptr; } + std::pmr::memory_resource* getMaybeFrameworkHostResource(bool forceHost = false) { return (hasFrameworkAllocator() && !forceHost) ? mExtMemoryPool.get() : mMemoryPool.get(); } + // Propagator const o2::base::PropagatorImpl* getDevicePropagator() const { return mPropagatorDevice; } virtual void setDevicePropagator(const o2::base::PropagatorImpl*) {}; @@ -309,7 +299,7 @@ struct TimeFrame { 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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 4c903ed1f3ca1..3ea382c626fed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -72,6 +72,7 @@ class Tracker bool isMatLUT() const { return mTraits->isMatLUT(); } void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } void printSummary() const; + void computeTracksMClabels(); private: void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } @@ -84,7 +85,6 @@ class Tracker // MC interaction void computeRoadsMClabels(); - void computeTracksMClabels(); void rectifyClusterIndices(); template diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index ee64cacb8fa2a..ddc32ed18cbfe 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -92,8 +92,9 @@ 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); bool mApplySmoothing = false; std::shared_ptr mMemoryPool; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 6c4ecc5ab424d..0529bd53f2073 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -96,7 +96,9 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index df736b3c7efdb..202dc87f04237 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -186,7 +186,9 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode 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; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index ca28ee227df56..29fb4ac4c69b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -177,9 +177,9 @@ template void TimeFrame::resetROFrameData(size_t nRofs) { for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - deepVectorClear(mUnsortedClusters[iLayer], getMaybeExternalHostResource()); - deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeExternalHostResource()); - clearResizeBoundedVector(mROFramesClusters[iLayer], nRofs + 1, getMaybeExternalHostResource()); + deepVectorClear(mUnsortedClusters[iLayer], getMaybeFrameworkHostResource()); + deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeFrameworkHostResource()); + clearResizeBoundedVector(mROFramesClusters[iLayer], nRofs + 1, getMaybeFrameworkHostResource()); deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); if (iLayer < 2) { @@ -302,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(), getMaybeExternalHostResource(maxLayers != nLayers)); - clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeExternalHostResource(maxLayers != nLayers)); + 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), getMaybeExternalHostResource(maxLayers != nLayers)); + clearResizeBoundedArray(mIndexTables, mNrof * (trkParam.ZBins * trkParam.PhiBins + 1), getMaybeFrameworkHostResource(maxLayers != nLayers)); clearResizeBoundedVector(mLines, mNrof, mMemoryPool.get()); clearResizeBoundedVector(mTrackletClusters, mNrof, mMemoryPool.get()); @@ -344,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]); @@ -352,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); } } @@ -574,6 +575,7 @@ void TimeFrame::setMemoryPool(std::shared_ptr po initVector(v, useExternal); } }; + // these will only reside on the host for the cpu part initVector(mTotVertPerIteration); initContainers(mClusterExternalIndices); @@ -603,12 +605,19 @@ void TimeFrame::setMemoryPool(std::shared_ptr po initVector(mRoadLabels); initContainers(mTracksLabel); // these will use possibly an externally provided allocator - initContainers(mClusters, hasExternalHostAllocator()); - initContainers(mUsedClusters, hasExternalHostAllocator()); - initContainers(mUnsortedClusters, hasExternalHostAllocator()); - initContainers(mIndexTables, hasExternalHostAllocator()); - initContainers(mTrackingFrameInfo, hasExternalHostAllocator()); - initContainers(mROFramesClusters, hasExternalHostAllocator()); + 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 @@ -639,7 +648,7 @@ void TimeFrame::wipe() deepVectorClear(mLines); // if we use the external host allocator then the assumption is that we // don't clear the memory ourself - if (!hasExternalHostAllocator()) { + if (!hasFrameworkAllocator()) { deepVectorClear(mClusters); deepVectorClear(mUsedClusters); deepVectorClear(mUnsortedClusters); @@ -659,5 +668,9 @@ void TimeFrame::wipe() } 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 59459dcab17e8..658a90b37613f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -145,8 +145,13 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } catch (const std::bad_alloc& err) { handleException(err); return; - } catch (...) { - error("Uncaught exception, all bets are off..."); + } 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()) { @@ -357,5 +362,9 @@ void Tracker::printSummary() const } 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 3a58ad1c000b7..b4ac847863d51 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -357,7 +357,7 @@ void TrackerTraits::computeLayerCells(const int iteration) break; } - if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer + iC] * constants::Radl * constants::Rho, true)) { break; } @@ -763,26 +763,36 @@ void TrackerTraits::findRoads(const int iteration) bounded_vector tracks(mMemoryPool.get()); mTaskArena->execute([&] { auto forSeed = [&](auto Tag, int iSeed, int offset = 0) { - const auto& seed{trackSeeds[iSeed]}; - TrackITSExt temporaryTrack{seed}; - temporaryTrack.resetCovariance(); - temporaryTrack.setChi2(0); - for (int iL{0}; iL < nLayers; ++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); + 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); + 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); @@ -1045,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(); @@ -1054,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, 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; + 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; @@ -1077,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); @@ -1194,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 @@ -1257,5 +1327,9 @@ void TrackerTraits::setNThreads(int n, std::shared_ptr } 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/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index a546056075700..0c4ecb0b12df1 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -530,11 +530,12 @@ void VertexerTraits::addTruthSeedingVertices() } Vertex vert; vert.setTimeStamp(rofId); - vert.setNContributors(std::ranges::count_if(mcReader.getTracks(iSrc, iEve), [](const auto& trk) { - return trk.isPrimary() && trk.GetPt() > 0.2 && std::abs(trk.GetEta()) < 1.3; - })); + // 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); + 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); 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 4ecc98eed9cfb..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/CookedTrackerSpec.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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 "ITStracking/BoundedAllocator.h" -#include "DataFormatsParameters/GRPObject.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "Framework/Task.h" -#include "TStopwatch.h" -#include "DetectorsBase/GRPGeomHelper.h" - -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -class CookedTrackerDPL : public Task -{ - public: - CookedTrackerDPL(std::shared_ptr gr, bool useMC, int trgType, TrackingMode::Type 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::Type 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; - std::shared_ptr mMemoryPool; - std::shared_ptr mTaskArena; - TStopwatch mTimer; -}; - -/// create a processor spec -/// run ITS CookedMatrix tracker -framework::DataProcessorSpec getCookedTrackerSpec(bool useMC, bool useGeom, int useTrig, TrackingMode::Type trMode); - -} // namespace its -} // namespace o2 - -#endif /* O2_ITS_COOKEDTRACKERDPL */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h index 011ee6b88ff6f..1d5d829a6f79a 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h @@ -16,7 +16,7 @@ #include "Framework/WorkflowSpec.h" #include "ITStracking/Configuration.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" namespace o2 { @@ -26,9 +26,9 @@ namespace its namespace reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, bool useCMtracker, TrackingMode::Type 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/TrackerSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h index ee5ba4d5cc61c..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 { @@ -39,7 +45,7 @@ class TrackerDPL : public framework::Task int trgType, 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,7 +63,7 @@ class TrackerDPL : public framework::Task TStopwatch mTimer; }; -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); +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/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 b989a78e59b7c..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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/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, TrackingMode::Type 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); - mTaskArena = std::make_shared(nthreads); - mMemoryPool = std::make_unique(); - mVertexerPtr->setMemoryPool(mMemoryPool); - mVertexerPtr->setNThreads(nthreads, mTaskArena); - mVertexerTraitsPtr->setMemoryPool(mMemoryPool); -} - -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; - mTimeFrame.setMemoryPool(mMemoryPool); - - LOG(info) << "ITSCookedTracker pulled " << compClusters.size() << " clusters, in " << rofs.size() << " RO frames"; - - std::vector trackLabels; - if (mUseMC) { - mTracker.setMCTruthContainers(labels.get(), &trackLabels); - } - - 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->setParameters(TrackingMode::getVertexingParameters(mMode)); - 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 " << TrackingMode::toString(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, TrackingMode::Type trmode) -{ - 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, - trmode)}, - Options{{"nthreads", VariantType::Int, 1, {"Number of threads"}}}}; -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index f375eaf67c04f..9f8cb6c83ef99 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -12,10 +12,9 @@ /// @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" @@ -29,7 +28,6 @@ namespace o2::its::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, - bool useCMtracker, TrackingMode::Type trmode, const bool overrideBeamPosition, bool upstreamDigits, @@ -38,53 +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 != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { - if (useCMtracker) { - specs.emplace_back(o2::its::getCookedTrackerSpec(useMC, useGeom, useTrig, trmode)); - } else { - if (useGPUWF) { - o2::gpu::GPURecoWorkflowSpec::Config cfg{ - .itsTriggerType = useTrig, - .processMC = useMC, - .runITSTracking = true, - .itsOverrBeamEst = overrideBeamPosition, - }; + 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); + } - 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)); + 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{ - .name = "its-gpu-tracker", - .inputs = taskInputs, - .outputs = task->outputs(), - .algorithm = AlgorithmSpec{adoptTask(task)}, - .options = 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::getTrackerSpec(useMC, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); } if (!disableRootOutput) { specs.emplace_back(o2::its::getTrackWriterSpec(useMC)); 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/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index dbfd5edf839ae..12d84ca7ab6ad 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -28,7 +28,7 @@ TrackerDPL::TrackerDPL(std::shared_ptr gr, int trgType, 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} { @@ -78,7 +78,7 @@ void TrackerDPL::end() 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, TrackingMode::Type trMode, 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; diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx index 4b9053436d44c..8080883888d40 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx @@ -41,8 +41,7 @@ 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 (deprecated)"}}, // keep this around to not break scripts - {"trackerCM", o2::framework::VariantType::Bool, false, {"use trackerCM (default: trackerCA)"}}, + {"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,unset,off"}}, @@ -65,11 +64,10 @@ 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 useCMtracker = configcontext.options().get("trackerCM"); 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"); @@ -90,7 +88,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } } auto wf = o2::its::reco_workflow::getWorkflow(useMC, - useCMtracker, o2::its::TrackingMode::fromString(trmode), beamPosOVerride, extDigits, 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/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/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/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/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/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/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/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/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/TOF/base/src/Geo.cxx b/Detectors/TOF/base/src/Geo.cxx index 08cda68c6d12e..7df4c3a3537e1 100644 --- a/Detectors/TOF/base/src/Geo.cxx +++ b/Detectors/TOF/base/src/Geo.cxx @@ -1049,8 +1049,8 @@ void Geo::rotateToSector(Float_t* xyz, Int_t isector) 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] = {.848077e-01, 8.660254e-01, 6.427876e-01, 3.420202e-01, -4.371139e-08, -3.420201e-01, -6.427876e-01, -8.660254e-01, -9.848077e-01, -9.848077e-01, -8.660254e-01, -6.427875e-01, -3.420201e-01, 1.192488e-08, 3.420201e-01, 6.427875e-01, 8.660253e-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.000001e-01, 1.736483e-01, -1.736482e-01, -5.000000e-01, -7.660446e-01, -9.396927e-01, -1.000000e+00, -9.396926e-01, -7.660445e-01, -5.000002e-01, -1.736480e-01}; + 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]; 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/TOF/workflow/src/make-parameter-collection.cxx b/Detectors/TOF/workflow/src/make-parameter-collection.cxx index c90f417639212..3a210df3fcad8 100644 --- a/Detectors/TOF/workflow/src/make-parameter-collection.cxx +++ b/Detectors/TOF/workflow/src/make-parameter-collection.cxx @@ -63,7 +63,7 @@ class ParamExample : public Parameters<5> public: ParamExample() : Parameters(std::array{"p0", "p1", "p2", "p3", "p4"}, "ParamExample") { setParameters(std::array{0, 1, 2, 3, 4}); }; // Default constructor with default parameters - ~ParamExample() override = default; + ~ParamExample() = default; }; int main(int argc, char* argv[]) diff --git a/Detectors/TPC/CMakeLists.txt b/Detectors/TPC/CMakeLists.txt index e3de1ca57c1be..aea0dee361874 100644 --- a/Detectors/TPC/CMakeLists.txt +++ b/Detectors/TPC/CMakeLists.txt @@ -10,6 +10,7 @@ # or submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(baserecsim) add_subdirectory(reconstruction) add_subdirectory(calibration) add_subdirectory(simulation) 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 100% rename from Detectors/TPC/base/include/TPCBase/Painter.h rename to Detectors/TPC/baserecsim/include/TPCBaseRecSim/Painter.h 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 06f6a360670dc..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" 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 99% rename from Detectors/TPC/base/src/Painter.cxx rename to Detectors/TPC/baserecsim/src/Painter.cxx index ffbc149225212..a571b50607dd2 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/baserecsim/src/Painter.cxx @@ -41,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" 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 566558b7e982f..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 @@ -28,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(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, uint8_t nextraRes = 0) : idxFirstResidual(idx), nResiduals(nRes), sourceId(source), nExtDetResid(nextraRes) {} + 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 + 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, 2); + + 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 @@ -149,11 +211,22 @@ struct TrackData { 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; } - ClassDefNV(TrackData, 9); + 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 @@ -239,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 @@ -266,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 @@ -294,10 +371,13 @@ class TrackInterpolation 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); + 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; } @@ -306,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 @@ -328,6 +414,7 @@ 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 @@ -337,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 @@ -344,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 @@ -352,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 d89b3c28f1a0d..cd5e3960160a6 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -33,6 +33,11 @@ #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 @@ -121,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(); @@ -135,7 +142,7 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da 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)); @@ -221,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); @@ -281,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); } } } @@ -312,6 +323,10 @@ void TrackInterpolation::process() // set the input containers mTPCTracksClusIdx = mRecoCont->getTPCTracksClusterRefs(); mTPCClusterIdxStruct = &mRecoCont->getTPCClusters(); + 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"; @@ -350,6 +365,7 @@ void TrackInterpolation::process() 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) { @@ -360,13 +376,13 @@ 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; @@ -431,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(); @@ -450,9 +468,12 @@ 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) { @@ -461,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; @@ -474,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 @@ -620,10 +649,19 @@ 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; + + 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]); - trackData.deltaTOF = tofMatch.getSignal() - tofMatch.getFT0Best() - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); - trackData.clAvailTOF = uint16_t(tofMatch.getFT0BestRes()); + 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; } @@ -640,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; @@ -649,12 +686,38 @@ 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? @@ -663,7 +726,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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); + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib); if (res == -1) { // no traklet on this layer continue; } @@ -677,6 +740,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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++; } } @@ -703,8 +767,16 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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; @@ -731,6 +803,33 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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++; } } @@ -739,7 +838,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) } mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); + 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); @@ -757,7 +856,8 @@ 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) + 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); @@ -799,6 +899,12 @@ int TrackInterpolation::processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLa trkData->TRDTrkltSlope[iLayer] = slope * 0x7fff / param::MaxTRDSlope; } } + if (trk64) { + *trk64 = trdTrklt; + } + if (trkCalib) { + *trkCalib = trdSP; + } return trkltDet; } @@ -808,6 +914,8 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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()); @@ -826,6 +934,9 @@ 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]; @@ -835,6 +946,8 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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; @@ -866,16 +979,20 @@ 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 reesiduals than possible ({})", clusterResiduals.size()); + LOGP(warn, "Extrapolated ITS-TPC track and found more residuals than possible ({})", clusterResiduals.size()); return; } @@ -899,7 +1016,6 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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; @@ -907,12 +1023,38 @@ void TrackInterpolation::extrapolateTrack(int iSeed) const auto z = clusterResiduals[iCl].z; 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, 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? @@ -924,7 +1066,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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); + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib); if (res == -1) { // no traklet on this layer continue; } @@ -939,6 +1081,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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++; } } @@ -948,8 +1091,12 @@ void TrackInterpolation::extrapolateTrack(int iSeed) trackData.clAvailTOF = 0; while (gidTableFull[GTrackID::TOF].isIndexSet() && !stopPropagation) { const auto& tofMatch = mRecoCont->getTOFMatch(gidFull); - trackData.deltaTOF = tofMatch.getSignal() - tofMatch.getFT0Best() - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); - trackData.clAvailTOF = uint16_t(tofMatch.getFT0BestRes()); + 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()}; @@ -971,6 +1118,13 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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; @@ -987,7 +1141,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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)) { + 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; @@ -997,6 +1151,33 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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++; } } @@ -1005,7 +1186,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) } mTrackData.push_back(std::move(trackData)); mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); (*trackDataExtended).nExtDetResid = trackData.nExtDetResid; @@ -1393,6 +1574,7 @@ void TrackInterpolation::reset() mTrackDataCompact.clear(); mTrackDataExtended.clear(); mClRes.clear(); + mDetInfoRes.clear(); mTrackDataUnfiltered.clear(); mClResUnfiltered.clear(); mGIDsSuccess.clear(); @@ -1403,6 +1585,8 @@ void TrackInterpolation::reset() mGIDtables.clear(); mTrackTimes.clear(); mSeeds.clear(); + mITSRefitSeedID.clear(); + mTrackPVID.clear(); } //______________________________________________ @@ -1416,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/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/CorrectionMapsLoader.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h index a907b83fe49bf..5a11ce3ea24e5 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h @@ -79,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/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 11f83f1c7189e..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" 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 4eb29c8833565..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()); diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index 0e4a5e2a73df4..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"); @@ -317,6 +345,7 @@ void CorrectionMapsLoader::copySettings(const CorrectionMapsLoader& src) mLumiCTPSource = src.mLumiCTPSource; mLumiScaleMode = src.mLumiScaleMode; mScaleInverse = src.getScaleInverse(); + mIDC2CTPFallbackActive = src.mIDC2CTPFallbackActive; } void CorrectionMapsLoader::updateInverse() 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 2de4ee2086426..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/CDBTypes.h" +#include "TPCBaseRecSim/CDBTypes.h" #include "Framework/ProcessingContext.h" #include "DataFormatsTPC/DCS.h" #include "Framework/InputRecord.h" 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/VDriftHelper.cxx b/Detectors/TPC/calibration/src/VDriftHelper.cxx index 71c4e50a63fcf..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" 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/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/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/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 3e196fa9bb7cc..5c66e4635987f 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) 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 @@ -69,10 +69,10 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) 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/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 cd2802b975fd2..60d7c28b8c74e 100644 --- a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx +++ b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx @@ -331,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; @@ -370,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); @@ -428,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; @@ -473,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); @@ -512,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/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/RecoWorkflow.h b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h index a5368f451a820..8e8a6a96eed63 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h @@ -85,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 2491e5f71a889..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" 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/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 4c2a26da38908..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; 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/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 98a9841fac8b2..3054dd5d61519 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -101,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 { @@ -507,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::CompClustersFlatForEncode, selIR)); + specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlatForEncode, selIR, ctfdictOpt)); } ////////////////////////////////////////////////////////////////////////////////////////////// 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/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 5007019d52910..ee3acc808ccb7 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -210,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(); @@ -289,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}; } } } @@ -1122,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); @@ -1512,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 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-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 3c8804de8b536..07b1c293bff98 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -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)"}}, }; @@ -182,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/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/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/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/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h index b4bfbddf7a4e6..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" @@ -94,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/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 598ce3c35c98c..f2d4aad829fe5 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -43,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" @@ -103,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); @@ -112,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(); @@ -127,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/DataFormatsECal/src/Cluster.cxx b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx index 77f7d9219ef6b..f792344f1f50f 100644 --- a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx @@ -50,7 +50,8 @@ int Cluster::getMcTrackID() const TLorentzVector Cluster::getMomentum() const { double r = std::sqrt(mX * mX + mY * mY + mZ * mZ); - if (r == 0) + if (r == 0) { return TLorentzVector(); + } return TLorentzVector(mE * mX / r, mE * mY / r, mE * mZ / r, mE); } 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/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h similarity index 51% rename from Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h rename to Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h index 3bbab66d16497..428a7a1f6d179 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.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 Constants.h +/// \brief General constants in FV0 +/// +/// \author Maciej Slupecki, University of Jyvaskyla, Finland -/// @file RawWorkflow.h - -#include "Framework/WorkflowSpec.h" +#ifndef ALICEO2_FD3_CONSTANTS_ +#define ALICEO2_FD3_CONSTANTS_ namespace o2 { -namespace fdd +namespace fd3 { -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut); -} // namespace fdd +struct Constants { + static constexpr unsigned int nsect = 8; + static constexpr unsigned int nringsA = 5; + static constexpr unsigned int nringsC = 6; + + static constexpr float etaMax = 7.0f; + static constexpr float etaMin = 4.0f; + + static constexpr unsigned int nringsA_withMG = 3; + static constexpr float etaMinA_withMG = 5.0f; +}; + +} // 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/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h similarity index 61% rename from DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx rename to Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h index 8affa29259f7a..8475ef2c77313 100644 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h @@ -9,11 +9,15 @@ // 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::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> + ; -ClassImp(o2::dataformats::EMCALChannelData; +#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/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h similarity index 70% rename from Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx rename to Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h index caa642794b561..83df03490ebd7 100644 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.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::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::fd3::Detector> + ; -namespace o2 -{ -namespace ft0 -{ - -} // namespace ft0 -} // namespace o2 +#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/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/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/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index a1e4b9321130f..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; } @@ -61,6 +69,8 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache 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; @@ -73,33 +83,56 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache 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 halfstave) 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 getHalfStave(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 + /// 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 /// \param int halfstave The half stave number for MLOT. Can be 0 or 1 - int getChipIndex(int subDetID, int petalcase, int disk, int lay, int stave, int halfstave) const; + /// \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 the chip index number from the subDetID, volume, layer, stave /// TODO: retrieve 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 - int getChipIndex(int subDetID, int volume, int lay, int stave, int halfstave) const; + /// \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 given the chip index number /// TODO: copute also from chip when chips will be available + /// 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 @@ -107,10 +140,12 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache /// \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 /// \param int halfstave The half stave number for MLOT. Can be 0 or 1 - bool getChipID(int index, int& subDetID, int& petalcase, int& disk, int& lay, int& stave, int& halfstave) const; + /// \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 @@ -127,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: @@ -141,36 +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::vector mNumberOfHalfStaves; ///< Number Of Staves in each stave of the 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 index 100af5be1b4d0..8110191931e44 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h @@ -35,18 +35,18 @@ class SegmentationChip // 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 three coordinate systems and one is transient. + // 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 (flat) coordinate system. This is the tube segment projected onto a flat - // surface. In the projection we implicitly assume that the inner and outer - // stretch does not depend on the radius. - // 3. The detector coordinate system. Defined by the row and column segmentation - // defined at the upper edge in the flat coord. + // 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 + // 2. The detector coordinate system. Defined by the row and column segmentation. // TODO: add segmentation for VD disks public: @@ -88,7 +88,7 @@ class SegmentationChip /// \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 (!isValidGlob(xRow, zCol, subDetID, layer)) { + if (!isValidLoc(xRow, zCol, subDetID, layer)) { LOGP(debug, "Local coordinates not valid: row = {} cm, col = {} cm", xRow, zCol); return false; } @@ -116,16 +116,11 @@ class SegmentationChip 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 && layer <= 3) { // ML + } else if (subDetID == 1) { pitchRow = PitchRowMLOT; pitchCol = PitchColMLOT; - maxWidth = constants::ML::width; - maxLength = constants::ML::length; - } else if (subDetID == 1 && layer >= 4) { // OT - pitchRow = PitchRowMLOT; - pitchCol = PitchColMLOT; - maxWidth = constants::OT::width; - maxLength = constants::OT::length; + 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)); @@ -133,25 +128,22 @@ class SegmentationChip }; // Check local coordinates (cm) validity. - static constexpr bool isValidGlob(float x, float z, int subDetID, int layer) noexcept + 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 && layer <= 3) { // ML - maxWidth = constants::ML::width; - maxLength = constants::ML::length; - } else if (subDetID == 1 && layer >= 4) { // OT - maxWidth = constants::OT::width; - maxLength = constants::OT::length; + } 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(float row, float col, int subDetID, int layer) noexcept + 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); @@ -159,14 +151,11 @@ class SegmentationChip 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 && layer <= 3) { // ML - nRows = constants::ML::nRows; - nCols = constants::ML::nCols; - } else if (subDetID == 1 && layer >= 4) { // OT - nRows = constants::OT::nRows; - nCols = constants::OT::nCols; + } else if (subDetID == 1) { + nRows = constants::moduleMLOT::chip::nRows; + nCols = constants::moduleMLOT::chip::nCols; } - return (row >= 0 && row < static_cast(nRows) && col >= 0 && col < static_cast(nCols)); + return (row >= 0 && row < nRows && col >= 0 && col < nCols); } /// Transformation from Detector cell coordinates to Geant detector centered @@ -191,7 +180,7 @@ class SegmentationChip 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 (!isValidGlob(xRow, zCol, subDetID, layer)) { + if (!isValidLoc(xRow, zCol, subDetID, layer)) { LOGP(debug, "Local coordinates not valid: row = {} cm, col = {} cm", xRow, zCol); return false; } @@ -207,12 +196,9 @@ class SegmentationChip 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 && layer <= 3) { // ML - xRow = 0.5 * (constants::ML::width - PitchRowMLOT) - (row * PitchRowMLOT); - zCol = col * PitchRowMLOT + 0.5 * (PitchRowMLOT - constants::ML::length); - } else if (subDetID == 1 && layer >= 4) { // OT - xRow = 0.5 * (constants::OT::width - PitchRowMLOT) - (row * PitchRowMLOT); - zCol = col * PitchColMLOT + 0.5 * (PitchColMLOT - constants::OT::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); } } @@ -263,17 +249,18 @@ class SegmentationChip } /// Print segmentation info - static const void Print() noexcept + 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 stave: " << constants::ML::nRows - << "\nOT stave: " << constants::OT::nRows; + << "\nML/OT chip: " << constants::moduleMLOT::chip::nRows; LOG(info) << "Number of cols:\nVD: " << constants::VD::petal::layer::nCols - << "\nML stave: " << constants::ML::nCols - << "\nOT stave: " << constants::OT::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; } }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index 373e9d972656b..c3c7de9dbe910 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -22,7 +22,7 @@ // Each TGeoShape has the following properties // length: dimension in z-axis // width: dimension in xy-axes -// color: for visulisation +// color: for visualisation namespace o2::trk::constants { // Default unit of TGeo = cm @@ -34,11 +34,11 @@ namespace VD // TODO: add a primitive segmentation with more granularity wrt 1/4 { namespace silicon { -constexpr double thickness{30 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 20 um substrate)? +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{0 * mu}; // thickness of the copper metal stack - for the moment it is not implemented +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 { @@ -49,8 +49,9 @@ 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, radii[1] * 2 * M_PI / 4, radii[2] * 2 * M_PI / 4}; // width of the quarter 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 @@ -76,21 +77,18 @@ constexpr double thickness{0 * mu}; // thickness of the copper metal stack - for } // 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 int nRows{static_cast(width / pitchX)}; // number of columns in the chip -constexpr int nCols{static_cast(length / pitchZ)}; // number of rows in the chip -constexpr double totalThickness{silicon::thickness + metalstack::thickness}; // total thickness of the chip -/// Set to 0 for the moment, to be adjusted with the actual design of the chip if needed -static constexpr float PassiveEdgeReadOut = 0.f; // width of the readout edge (Passive bottom) -static constexpr float PassiveEdgeTop = 0.f; // Passive area on top -static constexpr float PassiveEdgeSide = 0.f; // width of Passive area on left/right of the sensor +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{0.2 * mm}; // gap between the chips +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 @@ -102,16 +100,25 @@ constexpr int nCols{static_cast(length / chip::pitchZ)}; 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 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 -{ //// TODO: add shorter lenght of the stave of L4 -constexpr double width{moduleMLOT::width * 2}; // width of the stave -constexpr double length{moduleMLOT::length * 20}; // length of the stave +{ +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 @@ -124,6 +131,14 @@ 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 20088179f4dcc..d5d37ec00acef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -12,6 +12,9 @@ #include #include #include "TRKBase/SegmentationChip.h" +#include + +#include using Segmentation = o2::trk::SegmentationChip; @@ -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() @@ -67,26 +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); mNumberOfHalfStaves.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 + 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; @@ -101,14 +120,16 @@ void GeometryTGeo::Build(int loadTrans) /// filling the information for the MLOT for (int i = 0; i < mNumberOfLayersMLOT; i++) { - mNumberOfChipsPerLayerMLOT[i] = extractNumberOfStavesMLOT(i) * extractNumberOfHalfStavesMLOT(i); // for the moment, considering 1 half 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(numberOfChipsTotal); /// temporary, number of chips = number of staves and active parts + setSize(numberOfChipsTotal); fillMatrixCache(loadTrans); + defineMLOTSensors(); + fillTrackingFramesCacheMLOT(); } //__________________________________________________________________________ @@ -130,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++; } @@ -140,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 { @@ -156,7 +191,7 @@ int GeometryTGeo::getLayer(int index) const while (index > mLastChipIndex[lay]) { lay++; } - return lay - mNumberOfPetalsVD; /// numeration of MLOT layesrs starting from 0 + return lay - mNumberOfPetalsVD; /// numeration of MLOT layers starting from 0 } return -1; /// -1 if not found } @@ -172,9 +207,23 @@ int GeometryTGeo::getStave(int index) const } else if (subDetID == 1) { /// MLOT int lay = getLayer(index); index -= getFirstChipIndex(lay, petalcase, subDetID); // get the index of the sensing element in the layer - return index / mNumberOfHalfStaves[lay]; + + 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; } //__________________________________________________________________________ @@ -183,36 +232,89 @@ int GeometryTGeo::getHalfStave(int index) const int subDetID = getSubDetID(index); int lay = getLayer(index); int petalcase = getPetalCase(index); - int stave = getStave(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 - return index % 2; /// 0 = half stave left, 1 = half stave right, as geometry is filled /// TODO: generalize once chips will be in place. Can it be working also with chips? + + 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; /// not found + return -1; } //__________________________________________________________________________ -int GeometryTGeo::getDisk(int index) const +int GeometryTGeo::getModule(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]; + + 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, int halfstave) 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 @@ -220,41 +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 - if (mNumberOfHalfStaves[lay] == 2) { // staggered geometry - return getFirstChipIndex(lay, petalcase, subDetID) + stave * mNumberOfHalfStaves[lay] + halfstave; - } else if (mNumberOfHalfStaves[lay] == 1) { // turbo geometry - 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; } } - return -1; // not found + + 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 } //__________________________________________________________________________ -int GeometryTGeo::getChipIndex(int subDetID, int volume, int lay, int stave, int halfstave) const +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 - if (mNumberOfHalfStaves[lay] == 2) { // staggered geometry - return getFirstChipIndex(lay, -1, subDetID) + stave * mNumberOfHalfStaves[lay] + halfstave; - } else if (mNumberOfHalfStaves[lay] == 1) { // turbo geometry - return getFirstChipIndex(lay, -1, 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, -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, int& halfstave) 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; } @@ -263,34 +394,47 @@ bool GeometryTGeo::getChipID(int index, int& subDetID, int& petalcase, int& disk TString GeometryTGeo::getMatrixPath(int index) const { - int subDetID, petalcase, disk, layer, stave, halfstave; //// TODO: add chips in a second step - getChipID(index, subDetID, petalcase, disk, layer, stave, halfstave); + int subDetID, petalcase, disk, layer, stave, halfstave, mod, chip; + getChipID(index, subDetID, petalcase, disk, layer, stave, halfstave, mod, chip); - // PrintChipID(index, subDetID, petalcase, disk, layer, stave, halfstave); + // PrintChipID(index, subDetID, petalcase, disk, layer, stave, halfstave, mod, chip); // 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()); + // 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; + } + } + + // 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_%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_%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/", getTRKChipPattern(), layer, halfstave); // TRKChipx_0/1 - } else if (mNumberOfHalfStaves[layer] == 1) { // turbo geometry - path += Form("%s%d_1/", getTRKChipPattern(), layer); // TRKChipx_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_1/", getTRKSensorPattern(), layer); // TRKSensorx_1 + 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; } @@ -340,6 +484,32 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const return &matTmp; } +//__________________________________________________________________________ +void GeometryTGeo::defineMLOTSensors() +{ + for (int i = 0; i < mSize; i++) { + if (getSubDetID(i) == 0) { + continue; + } + sensorsMLOT.push_back(i); + } +} + +//__________________________________________________________________________ +void GeometryTGeo::fillTrackingFramesCacheMLOT() +{ + // fill for every sensor of ML & OT its tracking frame parameters + if (!isTrackingFrameCachedMLOT() && !sensorsMLOT.empty()) { + size_t newSize = sensorsMLOT.size(); + mCacheRefXMLOT.resize(newSize); + mCacheRefAlphaMLOT.resize(newSize); + for (int i = 0; i < newSize; i++) { + int sensorId = sensorsMLOT[i]; + extractSensorXAlphaMLOT(sensorId, mCacheRefXMLOT[i], mCacheRefAlphaMLOT[i]); + } + } +} + //__________________________________________________________________________ void GeometryTGeo::fillMatrixCache(int mask) { @@ -362,29 +532,51 @@ void GeometryTGeo::fillMatrixCache(int mask) } } + // build T2L matrices for ML & OT !! VD is yet to be implemented once its geometry will be more refined + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) && !getCacheT2L().isFilled()) { + LOGP(info, "Loading {} T2L matrices from TGeo for ML & OT", getName()); + if (sensorsMLOT.size()) { + int m_Size = sensorsMLOT.size(); + auto& cacheT2L = getCacheT2L(); + cacheT2L.setSize(m_Size); + for (int i = 0; i < m_Size; i++) { + int sensorID = sensorsMLOT[i]; + TGeoHMatrix& hm = createT2LMatrixMLOT(sensorID); + cacheT2L.setMatrix(Mat3D(hm), i); // here, sensorIDs from 0 to 374, sensorIDs shifted to 36 ! + } + } + } + // TODO: build matrices for the cases T2L, T2G and T2GRot when needed } //__________________________________________________________________________ -const char* GeometryTGeo::composeSymNameLayer(int d, int lr) +#ifdef ENABLE_UPGRADES +const char* GeometryTGeo::composeSymNameLayer(int d, int layer) { - return Form("%s/%s%d", composeSymNameTRK(d), getTRKLayerPattern(), lr); + return Form("%s/%s%d", composeSymNameTRK(d), getTRKLayerPattern(), layer); } +#endif -const char* GeometryTGeo::composeSymNameStave(int d, int lr) +const char* GeometryTGeo::composeSymNameStave(int d, int layer) { - return Form("%s/%s%d", composeSymNameLayer(d, lr), getTRKStavePattern(), lr); + return Form("%s/%s%d", composeSymNameLayer(d, layer), getTRKStavePattern(), layer); } -const char* GeometryTGeo::composeSymNameChip(int d, int lr) +const char* GeometryTGeo::composeSymNameModule(int d, int layer) { - return Form("%s/%s%d", composeSymNameStave(d, lr), getTRKChipPattern(), lr); + return Form("%s/%s%d", composeSymNameStave(d, layer), getTRKModulePattern(), layer); } -const char* GeometryTGeo::composeSymNameSensor(int d, int lr) +const char* GeometryTGeo::composeSymNameChip(int d, int layer) { - return Form("%s/%s%d", composeSymNameChip(d, lr), getTRKSensorPattern(), lr); + return Form("%s/%s%d", composeSymNameStave(d, layer), getTRKChipPattern(), layer); +} + +const char* GeometryTGeo::composeSymNameSensor(int d, int layer) +{ + return Form("%s/%s%d", composeSymNameChip(d, layer), getTRKSensorPattern(), layer); } //__________________________________________________________________________ @@ -449,118 +641,261 @@ int GeometryTGeo::extractNumberOfLayersMLOT() } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfActivePartsVD() const +int GeometryTGeo::extractNumberOfPetalsVD() const { - // The number of active parts returned here is 36 = 4 petals * (3 layers + 6 disks) - int numberOfParts = 0; + int numberOfPetals = 0; + TGeoVolume* trkV = gGeoManager->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; } - // 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; } - // 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; } - // 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; } - // 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; } @@ -569,27 +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; } - // 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; } @@ -643,15 +1033,71 @@ int GeometryTGeo::extractNumberOfHalfStavesMLOT(int lay) const for (int j = 0; j < nNodes; j++) { auto nd = dynamic_cast(nodes->At(j)); /// layer node const char* name = nd->GetName(); - if (strstr(name, getTRKChipPattern()) != nullptr) { + 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; } //__________________________________________________________________________ -void GeometryTGeo::PrintChipID(int index, int subDetID, int petalcase, int disk, int lay, int stave, int halfstave) const +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; @@ -661,6 +1107,8 @@ void GeometryTGeo::PrintChipID(int index, int subDetID, int petalcase, int disk, std::cout << "first chip index = " << getFirstChipIndex(lay, petalcase, subDetID) << std::endl; std::cout << "stave = " << stave << std::endl; std::cout << "halfstave = " << halfstave << std::endl; + std::cout << "module = " << mod << std::endl; + std::cout << "chip = " << chip << std::endl; } //__________________________________________________________________________ @@ -688,6 +1136,18 @@ void GeometryTGeo::Print(Option_t*) const 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()); std::cout << "mLastChipIndex = ["; @@ -708,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 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 ab817a3fdaa0d..10f117750d793 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -10,7 +10,8 @@ # 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 @@ -18,9 +19,8 @@ o2_add_library(TRKSimulation 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 @@ -28,14 +28,15 @@ o2_add_library(TRKSimulation O2::SimulationDataFormat) o2_target_root_dictionary(TRKSimulation - HEADERS include/TRKSimulation/ChipDigitsContainer.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 index 658fb823bb596..73c95b04c45e3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h @@ -29,6 +29,12 @@ class ChipDigitsContainer : public o2::itsmft::ChipDigitsContainer 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); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index 584ffaa3aff75..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 @@ -31,9 +30,6 @@ namespace trk class Detector : public o2::base::DetImpl { public: - static constexpr Int_t mNumberOfVolumes = 44; /// hardcoded for the current geometry = 8 MLOT layers + 36 volumes in the VD. TODO: automatize or change according to the current geometry - static constexpr Int_t mNumberOfVolumesVD = 36; /// hardcoded for the current geometry = 36 volumes in the VD. TODO: automatize or change according to the current geometry - Detector(bool active); Detector(); ~Detector(); @@ -46,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 { ; } @@ -61,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; @@ -71,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"); @@ -80,19 +76,21 @@ 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); @@ -106,14 +104,12 @@ class Detector : public o2::base::DetImpl public: static constexpr Int_t sNumberVDPetalCases = 4; //! Number of VD petals int getNumberOfLayers() const { return mLayers.size(); } //! Number of TRK layers - int getNumberOfLayersVD() const { return mPetalCases[0].mPetalLayers.size(); } - int getNumberOfDisksVD() const { return mPetalCases[0].mPetalDisks.size(); } - void Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int chipID) const; + 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 index 739ac5c9bd617..0463a68a77c3e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h @@ -16,7 +16,8 @@ #define ALICEO2_TRK_DIGIPARAMS_H #include -#include +#include "ITSMFTSimulation/AlpideSignalTrapezoid.h" +#include "ITSMFTSimulation/AlpideSimResponse.h" #include "TRKBase/TRKBaseParam.h" #include "TRKBase/GeometryTGeo.h" @@ -91,8 +92,8 @@ class DigiParams bool isTimeOffsetSet() const { return mTimeOffset > -infTime; } - const o2::trk::ChipSimResponse* getAlpSimResponse() const { return mAlpSimResponse; } - void setAlpSimResponse(const o2::trk::ChipSimResponse* par) { mAlpSimResponse = par; } + 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; } @@ -101,18 +102,18 @@ class DigiParams 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 = 150; ///< charge threshold in Nelectrons - int mMinChargeToAccount = 15; ///< minimum charge contribution to account - int mNSimSteps = 18; ///< number of steps in response simulation - float mNSimStepsInv = 0; ///< its inverse + 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 @@ -122,7 +123,7 @@ class DigiParams o2::itsmft::AlpideSignalTrapezoid mSignalShape; ///< signal timeshape parameterization - const o2::trk::ChipSimResponse* mAlpSimResponse = nullptr; //!< pointer on external response + std::unique_ptr mAlpSimResponse; //!< pointer on external response // auxiliary precalculated parameters float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns @@ -132,4 +133,4 @@ class DigiParams } // namespace trk } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h index 573217fe9b076..362de63fb8cb6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h @@ -25,7 +25,7 @@ #include "TRKSimulation/ChipDigitsContainer.h" #include "TRKSimulation/DigiParams.h" -#include "ITSMFTSimulation/Hit.h" +#include "TRKSimulation/Hit.h" #include "TRKBase/GeometryTGeo.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -45,16 +45,17 @@ class Digitizer 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::trk::DigiParams& getParams() { return (o2::trk::DigiParams&)mParams; } const o2::trk::DigiParams& getParams() const { return mParams; } void init(); - o2::trk::ChipSimResponse* getChipResponse(int chipID); + 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 { @@ -66,7 +67,6 @@ class Digitizer bool isContinuous() const { return mParams.isContinuous(); } void fillOutputContainer(uint32_t maxFrame = 0xffffffff); - void setDigiParams(const o2::trk::DigiParams& par) { mParams = par; } const o2::trk::DigiParams& getDigitParams() const { return mParams; } // provide the common trk::GeometryTGeo to access matrices and segmentation @@ -83,7 +83,7 @@ class Digitizer 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 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); @@ -102,15 +102,13 @@ class Digitizer /// 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 (for the moment, in the entire layer(VD) or stave (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 && layer <= 3) { // ML - return constants::ML::nCols; - } else if (subDetID == 1 && layer >= 4) { // OT - return constants::OT::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; } @@ -118,15 +116,13 @@ class Digitizer /// 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 (for the moment, in the entire layer(VD) or stave (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 && layer <= 3) { // ML - return constants::ML::nRows; - } else if (subDetID == 1 && layer >= 4) { // OT - return constants::OT::nRows; + } else if (subDetID == 1) { // ML/OT + return constants::moduleMLOT::chip::nRows; } return 0; } @@ -141,17 +137,18 @@ class Digitizer 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) int mNumberOfChips = 0; - o2::trk::ChipSimResponse* mChipSimResp = nullptr; // simulated response - o2::trk::ChipSimResponse* mChipSimRespVD = nullptr; // simulated response for VD chips - o2::trk::ChipSimResponse* mChipSimRespMLOT = nullptr; // simulated response for ML/OT chips + 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 mResponseFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; - std::string mResponseFile = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; /// using temporarly the APTS response + 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. 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/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index f5027310fa66d..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,47 +40,25 @@ 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(); } - mSensorName.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different naming scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12 - int VDvolume = 0; - for (int i = 0; i < 4; i++) { /// VD - for (int j = 0; j < 3; j++) { - mSensorName[VDvolume].Form("%s%d_%s%d_%s%d", GeometryTGeo::getTRKPetalPattern(), i, GeometryTGeo::getTRKPetalLayerPattern(), j, GeometryTGeo::getTRKSensorPattern(), j); - VDvolume++; - } - for (int j = 0; j < 6; j++) { - mSensorName[VDvolume].Form("%s%d_%s%d_%s%d", GeometryTGeo::getTRKPetalPattern(), i, GeometryTGeo::getTRKPetalDiskPattern(), j, GeometryTGeo::getTRKSensorPattern(), j); - VDvolume++; - } - } - - for (int i = 0; i < 8; i++) { /// MLOT - mSensorName[VDvolume].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i); - VDvolume++; - } - - for (auto vd : mSensorName) { - std::cout << "Volume name: " << vd << std::endl; - } - LOGP(info, "Summary of TRK configuration:"); for (auto& layer : mLayers) { LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer.getNumber(), layer.getName(), layer.getInnerRadius(), layer.getZ(), layer.getChipThickness()); @@ -102,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); @@ -179,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; } } @@ -260,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 < sNumberVDPetalCases; ++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); } @@ -279,7 +323,7 @@ void Detector::InitializeO2Detector() 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={}", i, mSensorID[i]); + LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data()); } } @@ -291,26 +335,18 @@ void Detector::defineSensitiveVolumes() TString volumeName; LOGP(info, "Adding TRK Sensitive Volumes"); - // Add petal case sensitive volumes - for (int petalCase = 0; petalCase < sNumberVDPetalCases; ++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); } } @@ -369,7 +405,6 @@ bool Detector::ProcessHits(FairVolume* vol) int subDetID = -1; int layer = -1; int volume = 0; - int stave = -1; int volID = vol->getMCid(); bool notSens = false; @@ -445,15 +480,25 @@ bool Detector::ProcessHits(FairVolume* vol) TLorentzVector positionStop; fMC->TrackPosition(positionStop); // Retrieve the indices with the volume path - int stave(0), halfstave(0); + int stave(0), halfstave(0), mod(0), chip(0); if (subDetID == 1) { - fMC->CurrentVolOffID(1, halfstave); - fMC->CurrentVolOffID(2, stave); + 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 - int chipID = mGeometryTGeo->getChipIndex(subDetID, volume, layer, stave, halfstave); + unsigned short chipID = mGeometryTGeo->getChipIndex(subDetID, volume, layer, stave, halfstave, mod, chip); + + // Print(vol, volume, subDetID, layer, stave, halfstave, mod, chip, chipID); - Print(vol, volume, subDetID, layer, stave, halfstave, chipID); + // mGeometryTGeo->Print(); Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(), mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), @@ -468,25 +513,27 @@ 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 chipID) const +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 1 " << fMC->CurrentVolOffName(1) << " halfstave: " << halfstave; - LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " stave: " << stave; + 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 2 " << fMC->CurrentVolOffName(2) << " stave: " << stave; + 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; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx index df6f46ac0ecb0..e2a78702204e5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx @@ -12,9 +12,10 @@ /// \file DigiParams.cxx /// \brief Implementation of the TRK digitization steering params. Based on the ITS2 code. -#include // for LOG -#include "TRKSimulation/DigiParams.h" #include +#include "Framework/Logger.h" +#include "TRKSimulation/DigiParams.h" +#include "TRKSimulation/ChipSimResponse.h" using namespace o2::trk; @@ -70,3 +71,14 @@ void DigiParams::print() const 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 cc89f0eff1a54..0fd8c7820ce28 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -26,7 +26,7 @@ #include // for LOG using o2::itsmft::Digit; -using o2::itsmft::Hit; +using o2::trk::Hit; using Segmentation = o2::trk::SegmentationChip; using namespace o2::trk; @@ -48,35 +48,42 @@ void Digitizer::init() mChips[i].setDeadChanMap(mDeadChanMap); } } - // importing the charge collection tables - // (initialized while building O2) - auto file = TFile::Open(mResponseFile.data()); - if (!file) { - LOG(fatal) << "Cannot open response file " << mResponseFile; - } - // setting the correct response function (for the moment, for both VD and MLOT the APTS response function is udes) - mChipSimResp = (o2::trk::ChipSimResponse*)file->Get("response1"); + // 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(debug) << " Depth max: " << mChipSimRespVD->getDepthMax(); - LOG(debug) << " Depth min: " << mChipSimRespVD->getDepthMin(); - - float thicknessVD = 0.0095; // cm --- hardcoded based on geometry currently present - float thicknessMLOT = 0.1; // cm --- hardcoded based on geometry currently present - - mSimRespVDScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowVD; - mSimRespVDScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColVD; - mSimRespVDScaleDepth = o2::trk::constants::apts::thickness / (thicknessVD); /// introducing this scaling factor because the silicon thickness for the moment is 1 mm -> rescale to 45 um which is the depth of the APTS response - // mSimRespVDShift = mChipSimRespVD->getDepthMax() - thicknessVD * mSimRespVDScaleDepth / 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 - 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; - mSimRespMLOTScaleDepth = o2::trk::constants::apts::thickness / (thicknessMLOT); /// introducing this scaling factor because the silicon thickness for the moment is 1 mm -> rescale to 45 um which is the depth of the APTS response - mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT * mSimRespMLOTScaleDepth / 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 + 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 @@ -84,7 +91,7 @@ void Digitizer::init() LOGP(info, "TRK Digitizer is initialised."); mParams.print(); - LOGP(info, "VD shift = {} ; ML/OT shift = {} = {} - {}", mSimRespVDShift, mSimRespMLOTShift, mChipSimRespMLOT->getDepthMax(), thicknessMLOT * mSimRespMLOTScaleDepth / 2.f); + 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"); @@ -92,7 +99,7 @@ void Digitizer::init() mIRFirstSampledTF = o2::raw::HBFUtils::Instance().getFirstSampledTFIR(); } -o2::trk::ChipSimResponse* Digitizer::getChipResponse(int chipID) +const o2::trk::ChipSimResponse* Digitizer::getChipResponse(int chipID) { if (mGeometry->getSubDetID(chipID) == 0) { /// VD return mChipSimRespVD; @@ -109,11 +116,11 @@ 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() << " (ID: " << mGeometry->getDetID() - << ") 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(); @@ -165,7 +172,7 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) mNewROFrame = nbc / mParams.getROFrameLengthInBC(); - LOG(info) << " NewROFrame " << mNewROFrame << " = " << nbc << "/" << mParams.getROFrameLengthInBC() << " (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; @@ -252,7 +259,7 @@ 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::trk::Hit& hit, uint32_t& maxFr, int evID, int srcID) { 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); @@ -333,17 +340,6 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID // std::cout<< "Example hit in local frame: " << exampleLoc << std::endl; // std::cout<<"Going back to glob coordinates: " << (matrix * exampleLoc) << std::endl; - //// adapting the depth (Y) of the chip to the APTS response maximum depth - LOG(debug) << "local original: startPos = " << xyzLocS << ", endPos = " << xyzLocE << std::endl; - if (subDetID == 0) { - xyzLocS.SetY(xyzLocS.Y() * mSimRespVDScaleDepth); - xyzLocE.SetY(xyzLocE.Y() * mSimRespVDScaleDepth); - } else { - xyzLocS.SetY(xyzLocS.Y() * mSimRespMLOTScaleDepth); - xyzLocE.SetY(xyzLocE.Y() * mSimRespMLOTScaleDepth); - } - LOG(debug) << "rescaled Y: startPos = " << xyzLocS << ", endPos = " << xyzLocE << std::endl; - math_utils::Vector3D step(xyzLocE); step -= xyzLocS; step *= nStepsInv; // position increment at each step @@ -422,7 +418,10 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID // 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--;) { @@ -479,7 +468,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID if (colDest < 0 || colDest >= colSpan) { continue; } - respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, flipCol); + respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, !flipCol); } } } @@ -487,7 +476,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID // fire the pixels assuming Poisson(n_response_electrons) o2::MCCompLabel lbl(hit.GetTrackID(), evID, srcID, false); auto roFrameAbs = mNewROFrame + roFrameRel; - LOG(debug) << "Spanning through rows and columns; rowspan = " << rowSpan << " colspan = " << colSpan << " = " << colE << " - " << colS << " +1 " << std::endl; + 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 @@ -495,9 +484,9 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID if (nEleResp <= 1.e-36) { continue; } - LOG(debug) << "nEleResp: value " << nEleResp << " for pixel " << irow << " " << icol << std::endl; + 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 << std::endl; + 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 " 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 cb6af1dcfc5b7..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 @@ -212,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 32d5bad87ce21..5bc053c516079 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C @@ -22,6 +22,7 @@ #include #include +#include "CCDB/BasicCCDBManager.h" #define ENABLE_UPGRADES #include "ITSMFTSimulation/AlpideSimResponse.h" #include "ITS3Simulation/ChipSimResponse.h" @@ -37,16 +38,12 @@ double cm2um(double cm) { return cm * 1e+4; } std::unique_ptr mAlpSimResp0, mAlpSimResp1, mAptSimResp1; -std::unique_ptr 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; - return nullptr; - } - auto base = f->Get(respName.c_str()); + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + o2::itsmft::AlpideSimResponse* base = cdb.get(path); if (!base) { - std::cerr << respName << " not found in " << fileName << std::endl; + std::cerr << path << " not found in " << '\n'; return nullptr; } return std::make_unique(base); @@ -54,24 +51,24 @@ std::unique_ptr loadResponse(const std::string& fileN 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/"); std::cout << "=====================\n"; LOGP(info, "ALPIDE Vbb=0V response"); - mAlpSimResp0 = loadResponse(AlpideFile, "response0"); // Vbb=0V + mAlpSimResp0 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbb0"); // Vbb=0V mAlpSimResp0->computeCentreFromData(); mAlpSimResp0->print(); LOGP(info, "Response Centre {}", mAlpSimResp0->getRespCentreDep()); std::cout << "=====================\n"; LOGP(info, "ALPIDE Vbb=-3V response"); - mAlpSimResp1 = loadResponse(AlpideFile, "response1"); // Vbb=-3V + mAlpSimResp1 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbbM3"); // Vbb=-3V mAlpSimResp1->computeCentreFromData(); mAlpSimResp1->print(); LOGP(info, "Response Centre {}", mAlpSimResp1->getRespCentreDep()); std::cout << "=====================\n"; LOGP(info, "APTS response"); - mAptSimResp1 = loadResponse(AptsFile, "response1"); // APTS + mAptSimResp1 = loadResponse("IT3/Calib/APTSResponse"); // APTS mAptSimResp1->computeCentreFromData(); mAptSimResp1->print(); LOGP(info, "Response Centre {}", mAptSimResp1->getRespCentreDep()); diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C b/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C index f245a047377ae..5e56321c7676d 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C @@ -283,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); @@ -295,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); @@ -307,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/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/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 8bfc7eedf2d6f..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]; @@ -77,7 +78,6 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, 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}; @@ -88,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()}, 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/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/Framework/TestWorkflows/src/dummy.cxx b/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyParam.cxx similarity index 83% rename from Framework/TestWorkflows/src/dummy.cxx rename to Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyParam.cxx index faff430964e73..00bb800e65f8c 100644 --- a/Framework/TestWorkflows/src/dummy.cxx +++ b/Detectors/Upgrades/ITS3/study/src/ITS3TrackingStudyParam.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 "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 592a34d94a3ca..010e1cd0a8127 100644 --- a/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ITS3/workflow/include/ITS3Workflow/RecoWorkflow.h @@ -16,16 +16,14 @@ #include "Framework/WorkflowSpec.h" #include "ITStracking/Configuration.h" -#include "GPUO2Interface.h" -#include "GPUReconstruction.h" -#include "GPUChainITS.h" +#include "GPUDataTypesConfig.h" namespace o2::its3::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, - o2::gpu::GPUDataTypes::DeviceType dtype, + 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 42f71b6ccebe0..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 { @@ -39,7 +45,7 @@ class TrackerDPL : public framework::Task int trgType, 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, its::TrackingMode::Type 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 8a1c1ef73cf2b..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, its::TrackingMode::Type 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,7 +40,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if (trmode != its::TrackingMode::Off) { diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index 7945f8e0af1df..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 { @@ -51,7 +44,7 @@ TrackerDPL::TrackerDPL(std::shared_ptr gr, int trgType, 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, its::TrackingMode::Type trMode, 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); diff --git a/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx index e4c78b3323a5e..018ad807a974a 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/its3-reco-workflow.cxx @@ -63,7 +63,7 @@ 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"); diff --git a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h b/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h deleted file mode 100644 index d5bc6631575af..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 "ReconstructionDataFormats/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/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/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/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/src/VisualisationEventROOTSerializer.cxx b/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx index 8c1a84c1bf85e..8480e15ee9772 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx @@ -97,6 +97,10 @@ void VisualisationEventROOTSerializer::toFile(const VisualisationEvent& event, L { 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/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 98% rename from Framework/Core/include/Framework/TableTreeHelpers.h rename to Framework/AnalysisSupport/include/Framework/TableTreeHelpers.h index 0a163d59aecb0..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 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 27dad43480913..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" @@ -62,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 @@ -185,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; @@ -242,14 +241,13 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) }; } -AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) +AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ctx*/) { - using namespace monitoring; - 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>>(); @@ -279,7 +277,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) callbacks.set(endofdatacb); return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { - auto mergePart = [&inputObjects, &objmap, &tskmap, &pc](DataRef const& ref) { + 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) { @@ -339,7 +337,9 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) 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; @@ -423,6 +423,16 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) // 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) { @@ -463,7 +473,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) }; 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 (int pi = 0; pi < pc.inputs().getNofParts(0); ++pi) { + 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/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/Core/src/TableTreeHelpers.cxx b/Framework/AnalysisSupport/src/TableTreeHelpers.cxx similarity index 100% rename from Framework/Core/src/TableTreeHelpers.cxx rename to Framework/AnalysisSupport/src/TableTreeHelpers.cxx 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/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index aba1f3ed4e13d..ea13d412cd0b8 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -18,7 +18,7 @@ #include "Framework/RawDeviceService.h" #include "Framework/Output.h" #include "Framework/Signpost.h" -#include "Framework/AnalysisContext.h" +#include "Framework/DanglingEdgesContext.h" #include "Framework/ConfigContext.h" #include "Framework/ConfigContext.h" #include @@ -67,37 +67,38 @@ void fillValidRoutes(CCDBFetcherHelper& helper, std::vector(); - std::vector> schemas; - auto schemaMetadata = std::make_shared(); + 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 : ac.analysisCCDBInputs) { - std::vector> fields; - schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); - schemaMetadata->Append("outputBinding", input.binding); + 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; + 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)); } - // 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)); } - schemas.emplace_back(std::make_shared(fields, schemaMetadata)); - } - return adaptStateful([schemas](CallbackService& callbacks, ConfigParamRegistry const& options, DeviceSpec const& spec) { + std::shared_ptr helper = std::make_shared(); CCDBFetcherHelper::initialiseHelper(*helper, options); std::unordered_map bindings; @@ -109,13 +110,13 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& ctx) 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 ref = inputs.get(inputBinding); - auto table = ref->asArrowTable(); + 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", @@ -128,11 +129,11 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& ctx) int outputRouteIndex = bindings.at(outRouteDesc); auto& spec = helper->routes[outputRouteIndex].matcher; std::vector> builders; - for (auto& _ : schema->fields()) { + for (auto const& _ : schema->fields()) { builders.emplace_back(std::make_shared()); } - for (size_t ci = 0; ci < timestampColumn->num_chunks(); ++ci) { + for (auto ci = 0; ci < timestampColumn->num_chunks(); ++ci) { std::shared_ptr chunk = timestampColumn->chunk(ci); auto const* timestamps = chunk->data()->GetValuesSafe(1); diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h index f8175034da0ba..3be2138bd2b5c 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h @@ -17,7 +17,7 @@ namespace o2::framework { struct AnalysisCCDBHelpers { - static AlgorithmSpec fetchFromCCDB(ConfigContext const& ctx); + static AlgorithmSpec fetchFromCCDB(ConfigContext const&); }; } // namespace o2::framework diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 43571526855cc..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 @@ -109,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 @@ -124,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 @@ -132,7 +134,6 @@ 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 @@ -143,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 @@ -224,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 @@ -237,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 @@ -250,6 +256,7 @@ 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 @@ -349,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 b9b97bfa5ca9c..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 @@ -34,7 +35,6 @@ #include #include #include // IWYU pragma: export -#include namespace o2::framework { @@ -53,6 +53,12 @@ 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 @@ -212,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 { @@ -238,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{})); } }; @@ -362,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; }; @@ -385,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 @@ -406,12 +426,6 @@ struct Binding { } }; -template -auto createFieldsFromColumns(framework::pack) -{ - return std::vector>{C::asArrowField()...}; -} - using SelectionVector = std::vector; template @@ -686,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 @@ -1269,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); }; @@ -1277,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(); @@ -1293,7 +1312,19 @@ concept with_ccdb_urls = requires { }; template -concept with_base_table = not_void>::metadata::base_table_t>; +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() @@ -1356,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 @@ -1365,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) { @@ -1399,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&&) { @@ -1449,7 +1519,10 @@ struct PreslicePolicyGeneral : public PreslicePolicyBase { 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; @@ -1457,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})}, {}} { } @@ -1492,7 +1565,11 @@ 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 @@ -1639,10 +1716,10 @@ 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)); if (t.tableSize() != 0) { @@ -1651,19 +1728,19 @@ auto doSliceByCached(T const* table, framework::expressions::BindingNode const& 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)); if (t.tableSize() != 0) { @@ -2650,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2660,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)) { \ @@ -2673,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 \ @@ -2846,7 +2908,7 @@ consteval auto getIndexTargets() o2::soa::Binding getCurrentRaw() const { return mBinding; } \ o2::soa::Binding mBinding; \ }; \ - [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_##Id { "fIndex" #_Table_ _Suffix_, _Name_##Id::hash, o2::framework::expressions::selectArrowType<_Type_>() } + [[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, "") @@ -3016,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; \ } \ @@ -3243,28 +3299,29 @@ 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_ diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index b174f3858e165..e3032830beaac 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -404,6 +404,16 @@ DECLARE_SOA_DYNAMIC_COLUMN(HasTOF, hasTOF, //! Flag to check if track has a TOF [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::TOF; }); DECLARE_SOA_DYNAMIC_COLUMN(IsPVContributor, isPVContributor, //! Run 3: Has this track contributed to the collision vertex fit [](uint8_t flags) -> bool { return (flags & o2::aod::track::PVContributor) == o2::aod::track::PVContributor; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideA, hasTPCSideA, //! Run 3: Has this track TPC clusters from side A? + [](uint8_t flags) -> bool { return (flags & o2::aod::track::TPCSideA) == o2::aod::track::TPCSideA; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideAOnly, hasTPCSideAOnly, //! Run 3: Has this track TPC clusters from side A only? + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == o2::aod::track::TPCSideA; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideC, hasTPCSideC, //! Run 3: Has this track TPC clusters from side C? + [](uint8_t flags) -> bool { return (flags & o2::aod::track::TPCSideC) == o2::aod::track::TPCSideC; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideCOnly, hasTPCSideCOnly, //! Run 3: Has this track TPC clusters from side C only? + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == o2::aod::track::TPCSideC; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCBothSides, hasTPCBothSides, //! Run 3: Has this track TPC clusters from both side A and C? + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == (o2::aod::track::TPCSideA || o2::aod::track::TPCSideC); }); DECLARE_SOA_DYNAMIC_COLUMN(PIDForTracking, pidForTracking, //! PID hypothesis used during tracking. See the constants in the class PID in PID.h [](uint32_t flags) -> uint32_t { return flags >> 28; }); DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsFound, tpcNClsFound, //! Number of found TPC clusters diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 842263cd75abc..a01d14b6632a9 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -26,6 +26,152 @@ #include "Framework/Traits.h" #include +namespace o2::soa +{ +struct IndexRecord { + std::string label; + framework::ConcreteDataMatcher matcher; + std::string columnLabel; + IndexKind kind; + int pos; + std::shared_ptr type = [](IndexKind kind) -> std::shared_ptr { + switch (kind) { + case IndexKind::IdxSingle: + case IndexKind::IdxSelf: + return arrow::int32(); + case IndexKind::IdxSlice: + return arrow::fixed_size_list(arrow::int32(), 2); + case IndexKind::IdxArray: + return arrow::list(arrow::int32()); + default: + return {nullptr}; + } + }(kind); + + auto operator==(IndexRecord const& other) const + { + return (this->label == other.label) && (this->columnLabel == other.columnLabel) && (this->kind == other.kind) && (this->pos == other.pos); + } + + std::shared_ptr field() const + { + return std::make_shared(columnLabel, type); + } +}; + +struct IndexBuilder { + static std::vector makeBuilders(std::vector>&& tables, std::vector const& records); + static void resetBuilders(std::vector& builders, std::vector>&& tables); + + static std::shared_ptr materialize(std::vector& builders, std::vector>&& tables, std::vector const& records, std::shared_ptr const& schema, bool exclusive); +}; +} // namespace o2::soa + +namespace o2::framework +{ +std::shared_ptr makeEmptyTableImpl(const char* name, std::shared_ptr& schema); + +template +auto makeEmptyTable(const char* name) +{ + auto schema = std::make_shared(soa::createFieldsFromColumns(typename T::table_t::persistent_columns_t{})); + return makeEmptyTableImpl(name, schema); +} + +template +auto makeEmptyTable() +{ + auto schema = std::make_shared(soa::createFieldsFromColumns(typename aod::MetadataTrait>::metadata::persistent_columns_t{})); + return makeEmptyTableImpl(o2::aod::label(), schema); +} + +template +auto makeEmptyTable(const char* name, framework::pack p) +{ + auto schema = std::make_shared(soa::createFieldsFromColumns(p)); + return makeEmptyTableImpl(name, schema); +} + +template +auto makeEmptyTable(const char* name) +{ + auto schema = std::make_shared(soa::createFieldsFromColumns(typename aod::MetadataTrait::metadata::persistent_columns_t{})); + return makeEmptyTableImpl(name, schema); +} + +std::shared_ptr spawnerHelper(std::shared_ptr const& fullTable, std::shared_ptr newSchema, size_t nColumns, + expressions::Projector* projectors, const char* name, std::shared_ptr& projector); + +std::shared_ptr spawnerHelper(std::shared_ptr const& fullTable, std::shared_ptr newSchema, + const char* name, size_t nColumns, + const std::shared_ptr& projector); + +/// Expression-based column generator to materialize columns +template + requires(soa::has_extension::metadata>) +auto spawner(std::shared_ptr const& fullTable, const char* name, o2::framework::expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) +{ + if (fullTable->num_rows() == 0) { + return makeEmptyTable(name); + } + constexpr auto Ncol = []() { + if constexpr (soa::has_configurable_extension) { + return framework::pack_size(typename M::placeholders_pack_t{}); + } else { + return framework::pack_size(typename M::expression_pack_t{}); + } + }.template operator()::metadata>(); + return spawnerHelper(fullTable, schema, Ncol, projectors, name, projector); +} + +template +auto spawner(framework::pack, std::vector>&& tables, const char* name, expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) +{ + std::array labels{"original"}; + auto fullTable = soa::ArrowHelpers::joinTables(std::move(tables), std::span{labels}); + if (fullTable->num_rows() == 0) { + return makeEmptyTable(name, framework::pack{}); + } + return spawnerHelper(fullTable, schema, sizeof...(C), projectors, name, projector); +} + +std::string serializeProjectors(std::vector& projectors); +std::string serializeSchema(std::shared_ptr schema); +std::string serializeIndexRecords(std::vector& irs); +std::vector> extractSources(ProcessingContext& pc, std::vector const& labels); + +struct Spawner { + std::string binding; + std::vector labels; + std::vector matchers; + std::vector> expressions; + std::shared_ptr projector = nullptr; + std::shared_ptr schema = nullptr; + std::shared_ptr inputSchema = nullptr; + + header::DataOrigin origin; + header::DataDescription description; + header::DataHeader::SubSpecificationType version; + + std::shared_ptr materialize(ProcessingContext& pc) const; +}; + +struct Builder { + bool exclusive; + std::vector labels; + std::vector matchers; + std::vector records; + std::shared_ptr outputSchema; + header::DataOrigin origin; + header::DataDescription description; + header::DataHeader::SubSpecificationType version; + + std::shared_ptr> builders = nullptr; + + std::shared_ptr materialize(ProcessingContext& pc); +}; +} // namespace o2::framework + namespace o2::soa { template @@ -38,6 +184,16 @@ constexpr auto tableRef2ConfigParamSpec() {"\"\""}}; } +template +constexpr auto tableRef2Schema() +{ + return o2::framework::ConfigParamSpec{ + std::string{"input-schema:"} + o2::aod::label(), + framework::VariantType::String, + framework::serializeSchema(o2::aod::MetadataTrait>::metadata::getSchema()), + {"\"\""}}; +} + namespace { template @@ -50,6 +206,16 @@ inline constexpr auto getSources() }.template operator()(); } +template +inline constexpr auto getSourceSchemas() +{ + return [] refs>() { + return [](std::index_sequence) { + return std::vector{soa::tableRef2Schema()...}; + }(std::make_index_sequence()); + }.template operator()(); +} + template inline constexpr auto getCCDBUrls() { @@ -63,15 +229,66 @@ inline constexpr auto getCCDBUrls() return result; } +template + requires(std::same_as) +consteval IndexKind getIndexKind() +{ + return IndexKind::IdxSingle; +} + +template + requires(std::is_bounded_array_v) +consteval IndexKind getIndexKind() +{ + return IndexKind::IdxSlice; +} + +template + requires(framework::is_specialization_v) +consteval IndexKind getIndexKind() +{ + return IndexKind::IdxArray; +} + +template +inline constexpr auto getIndexMapping() +{ + std::vector idx; + using indices = T::index_pack_t; + using Key = T::Key; + [&idx](std::index_sequence) mutable { + constexpr auto refs = T::sources; + ([&idx]() mutable { + constexpr auto pos = o2::aod::MetadataTrait>::metadata::template getIndexPosToKey(); + if constexpr (pos == -1) { + idx.emplace_back(o2::aod::label(), o2::aod::matcher(), C::columnLabel(), IndexKind::IdxSelf, pos); + } else { + idx.emplace_back(o2::aod::label(), o2::aod::matcher(), C::columnLabel(), getIndexKind(), pos); + } + }.template operator()>(), + ...); + }(std::make_index_sequence()); + ; + return idx; +} + template constexpr auto getInputMetadata() -> std::vector { std::vector inputMetadata; + auto inputSources = getSources(); std::sort(inputSources.begin(), inputSources.end(), [](framework::ConfigParamSpec const& a, framework::ConfigParamSpec const& b) { return a.name < b.name; }); auto last = std::unique(inputSources.begin(), inputSources.end(), [](framework::ConfigParamSpec const& a, framework::ConfigParamSpec const& b) { return a.name == b.name; }); inputSources.erase(last, inputSources.end()); inputMetadata.insert(inputMetadata.end(), inputSources.begin(), inputSources.end()); + + auto inputSchemas = getSourceSchemas(); + std::sort(inputSchemas.begin(), inputSchemas.end(), [](framework::ConfigParamSpec const& a, framework::ConfigParamSpec const& b) { return a.name < b.name; }); + last = std::unique(inputSchemas.begin(), inputSchemas.end(), [](framework::ConfigParamSpec const& a, framework::ConfigParamSpec const& b) { return a.name == b.name; }); + inputSchemas.erase(last, inputSchemas.end()); + inputMetadata.insert(inputMetadata.end(), inputSchemas.begin(), inputSchemas.end()); + return inputMetadata; } @@ -97,6 +314,44 @@ constexpr auto getCCDBMetadata() -> std::vector { return {}; } + +template +constexpr auto getExpressionMetadata() -> std::vector +{ + using expression_pack_t = T::expression_pack_t; + + auto projectors = [](framework::pack) -> std::vector { + std::vector result; + (result.emplace_back(std::move(C::Projector())), ...); + return result; + }(expression_pack_t{}); + + auto json = framework::serializeProjectors(projectors); + return {framework::ConfigParamSpec{"projectors", framework::VariantType::String, json, {"\"\""}}}; +} + +template + requires(!soa::with_expression_pack) +constexpr auto getExpressionMetadata() -> std::vector +{ + return {}; +} + +template +constexpr auto getIndexMetadata() -> std::vector +{ + auto map = getIndexMapping(); + return {framework::ConfigParamSpec{"index-records", framework::VariantType::String, framework::serializeIndexRecords(map), {"\"\""}}, + {framework::ConfigParamSpec{"index-exclusive", framework::VariantType::Bool, T::exclusive, {"\"\""}}}}; +} + +template + requires(!soa::with_index_pack) +constexpr auto getIndexMetadata() -> std::vector +{ + return {}; +} + } // namespace template @@ -107,6 +362,13 @@ constexpr auto tableRef2InputSpec() metadata.insert(metadata.end(), m.begin(), m.end()); auto ccdbMetadata = getCCDBMetadata>::metadata>(); metadata.insert(metadata.end(), ccdbMetadata.begin(), ccdbMetadata.end()); + auto p = getExpressionMetadata>::metadata>(); + metadata.insert(metadata.end(), p.begin(), p.end()); + auto idx = getIndexMetadata>::metadata>(); + metadata.insert(metadata.end(), idx.begin(), idx.end()); + if constexpr (!soa::with_ccdb_urls>::metadata>) { + metadata.emplace_back(framework::ConfigParamSpec{"schema", framework::VariantType::String, framework::serializeSchema(o2::aod::MetadataTrait>::metadata::getSchema()), {"\"\""}}); + } return framework::InputSpec{ o2::aod::label(), @@ -285,29 +547,29 @@ struct TableTransform { constexpr static auto sources = M::sources; template - static constexpr auto base_spec() + static auto base_spec() { return soa::tableRef2InputSpec(); } static auto base_specs() { - return [](std::index_sequence) -> std::vector { - return {base_spec()...}; + return [](std::index_sequence) { + return std::array{base_spec()...}; }(std::make_index_sequence{}); } - constexpr auto spec() const + static constexpr auto spec() { return soa::tableRef2OutputSpec(); } - constexpr auto output() const + static constexpr auto output() { return soa::tableRef2Output(); } - constexpr auto ref() const + static constexpr auto ref() { return soa::tableRef2OutputRef(); } @@ -333,15 +595,9 @@ struct Spawns : decltype(transformBase()) { using spawnable_t = T; using metadata = decltype(transformBase())::metadata; using extension_t = typename metadata::extension_table_t; - using base_table_t = typename metadata::base_table_t; using expression_pack_t = typename metadata::expression_pack_t; static constexpr size_t N = framework::pack_size(expression_pack_t{}); - constexpr auto pack() - { - return expression_pack_t{}; - } - typename T::table_t* operator->() { return table.get(); @@ -355,6 +611,7 @@ struct Spawns : decltype(transformBase()) { { return extension->asArrowTable(); } + std::shared_ptr table = nullptr; std::shared_ptr extension = nullptr; std::array projectors = [](framework::pack) -> std::array @@ -363,13 +620,17 @@ struct Spawns : decltype(transformBase()) { } (expression_pack_t{}); std::shared_ptr projector = nullptr; - std::shared_ptr schema = std::make_shared(o2::soa::createFieldsFromColumns(expression_pack_t{})); + std::shared_ptr schema = []() { + auto s = std::make_shared(o2::soa::createFieldsFromColumns(expression_pack_t{})); + s->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{o2::aod::label()}})); + return s; + }(); }; template concept is_spawns = requires(T t) { typename T::metadata; - requires std::same_as; + typename T::expression_pack_t; requires std::same_as>; }; @@ -384,15 +645,9 @@ struct Defines : decltype(transformBase()) { using spawnable_t = T; using metadata = decltype(transformBase())::metadata; using extension_t = typename metadata::extension_table_t; - using base_table_t = typename metadata::base_table_t; using placeholders_pack_t = typename metadata::placeholders_pack_t; static constexpr size_t N = framework::pack_size(placeholders_pack_t{}); - constexpr auto pack() - { - return placeholders_pack_t{}; - } - typename T::table_t* operator->() { return table.get(); @@ -411,7 +666,11 @@ struct Defines : decltype(transformBase()) { std::array projectors; std::shared_ptr projector = nullptr; - std::shared_ptr schema = std::make_shared(o2::soa::createFieldsFromColumns(placeholders_pack_t{})); + std::shared_ptr schema = []() { + auto s = std::make_shared(o2::soa::createFieldsFromColumns(placeholders_pack_t{})); + s->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{o2::aod::label()}})); + return s; + }(); std::shared_ptr inputSchema = nullptr; bool needRecompilation = false; @@ -428,7 +687,7 @@ using DefinesDelayed = Defines; template concept is_defines = requires(T t) { typename T::metadata; - requires std::same_as; + typename T::placeholders_pack_t; requires std::same_as>; requires std::same_as; &T::recompile; @@ -443,129 +702,6 @@ struct Exclusive { struct Sparse { }; -namespace -{ -template -inline std::shared_ptr getIndexToKey(arrow::Table* table) -{ - using IC = framework::pack_element_t(typename T::external_index_columns_t{}), typename T::external_index_columns_t>; - return table->column(framework::has_type_at_v(typename T::persistent_columns_t{})); -} - -template -struct ColumnTrait { - using column_t = C; - - static consteval auto listSize() - { - if constexpr (std::same_as>) { - return -1; - } else if constexpr (std::same_as) { - return 2; - } else { - return 1; - } - } - - template - static std::shared_ptr makeColumnBuilder(arrow::Table* table, arrow::MemoryPool* pool) - { - if constexpr (!std::same_as) { - return std::make_shared(getIndexToKey(table), C::columnLabel(), listSize(), pool); - } else { - return std::make_shared(C::columnLabel(), pool); - } - } -}; - -template -struct Reduction { - using type = typename std::conditional(), SelfIndexColumnBuilder, IndexColumnBuilder>::type; -}; - -template -using reduced_t = Reduction::type; -} // namespace - -template -struct IndexBuilder { - template refs, typename C1, typename... Cs> - static auto indexBuilder(const char* label, std::vector>&& tables, framework::pack) - { - auto pool = arrow::default_memory_pool(); - SelfIndexColumnBuilder self{C1::columnLabel(), pool}; - std::unique_ptr keyIndex = nullptr; - if constexpr (!Key::template hasOriginal()) { - keyIndex = std::make_unique(tables[0]->column(o2::aod::MetadataTrait>::metadata::template getIndexPosToKey())); - } - - auto sq = std::make_index_sequence(); - - auto columnBuilders = [&tables, &pool ](std::index_sequence) -> std::array, sizeof...(Cs)> - { - return {[](arrow::Table* table, arrow::MemoryPool* pool) { - using T = framework::pack_element_t>; - if constexpr (!Key::template hasOriginal()) { - constexpr auto pos = o2::aod::MetadataTrait>::metadata::template getIndexPosToKey(); - return std::make_shared(table->column(pos), T::columnLabel(), ColumnTrait::listSize(), pool); - } else { - return std::make_shared(T::columnLabel(), pool); - } - }(tables[Is + 1].get(), pool)...}; - } - (sq); - - std::array finds; - - for (int64_t counter = 0; counter < tables[0]->num_rows(); ++counter) { - int64_t idx = -1; - if constexpr (Key::template hasOriginal()) { - idx = counter; - } else { - idx = keyIndex->valueAt(counter); - } - finds = [&idx, &columnBuilders](std::index_sequence) { - return std::array{ - [&idx, &columnBuilders]() { - using T = typename framework::pack_element_t>; - return std::static_pointer_cast>(columnBuilders[Is])->template find(idx); - }()...}; - }(sq); - if constexpr (std::same_as) { - [&idx, &columnBuilders](std::index_sequence) { - ([&idx, &columnBuilders]() { - using T = typename framework::pack_element_t>; - return std::static_pointer_cast>(columnBuilders[Is])->template fill(idx); }(), ...); - }(sq); - self.fill(counter); - } else if constexpr (std::same_as) { - if (std::none_of(finds.begin(), finds.end(), [](bool const x) { return x == false; })) { - [&idx, &columnBuilders](std::index_sequence) { - ([&idx, &columnBuilders]() { - using T = typename framework::pack_element_t>; - return std::static_pointer_cast>(columnBuilders[Is])->template fill(idx); - }(), - ...); - }(sq); - self.fill(counter); - } - } - } - - return [&label, &columnBuilders, &self](std::index_sequence) { - return makeArrowTable(label, - {self.template result(), [&columnBuilders]() { - using T = typename framework::pack_element_t>; - return std::static_pointer_cast>(columnBuilders[Is])->template result(); - }()...}, - {self.field(), [&columnBuilders]() { - using T = typename framework::pack_element_t>; - return std::static_pointer_cast>(columnBuilders[Is])->field(); - }()...}); - }(sq); - } -}; - /// This helper struct allows you to declare index tables to be created in a task template @@ -579,12 +715,17 @@ template struct Builds : decltype(transformBase()) { using buildable_t = T; using metadata = decltype(transformBase())::metadata; - using IP = std::conditional_t, IndexBuilder>; using Key = metadata::Key; using H = typename T::first_t; using Ts = typename T::rest_t; using index_pack_t = metadata::index_pack_t; + std::shared_ptr outputSchema = []() { return std::make_shared(soa::createFieldsFromColumns(index_pack_t{}))->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{o2::aod::label()}})); }(); + + std::vector map = soa::getIndexMapping(); + + std::vector builders; + T* operator->() { return table.get(); @@ -605,10 +746,9 @@ struct Builds : decltype(transformBase()) { return index_pack_t{}; } - template - auto build(framework::pack, std::vector>&& tables) + auto build(std::vector>&& tables) { - this->table = std::make_shared(IP::template indexBuilder(o2::aod::label(), std::forward>>(tables), framework::pack{})); + this->table = std::make_shared(soa::IndexBuilder::materialize(builders, std::forward>>(tables), map, outputSchema, metadata::exclusive)); return (this->table != nullptr); } }; @@ -617,7 +757,7 @@ template concept is_builds = requires(T t) { typename T::metadata; typename T::Key; - requires std::same_as; + requires std::same_as>; }; /// This helper class allows you to declare things which will be created by a diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index 596f3da6a557a..121ce7f4b4a77 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -34,23 +34,11 @@ namespace o2::framework namespace { -template -static inline auto extractOriginal(ProcessingContext& pc) -{ - return pc.inputs().get(aod::MetadataTrait::metadata::tableLabel())->asArrowTable(); -} - -template -static inline std::vector> extractOriginals(framework::pack, ProcessingContext& pc) -{ - return {extractOriginal(pc)...}; -} - template refs> static inline auto extractOriginals(ProcessingContext& pc) { return [&](std::index_sequence) -> std::vector> { - return {pc.inputs().get(o2::aod::label())->asArrowTable()...}; + return {pc.inputs().get(o2::aod::matcher())->asArrowTable()...}; }(std::make_index_sequence()); } } // namespace @@ -160,12 +148,12 @@ const char* controlOption() } template -concept with_base_table = requires(T const& t) { t.base_specs(); }; +concept with_base_table = requires { T::base_specs(); }; template -bool requestInputs(std::vector& inputs, T const& entity) +bool requestInputs(std::vector& inputs, T const& /*entity*/) { - auto base_specs = entity.base_specs(); + auto base_specs = T::base_specs(); for (auto base_spec : base_specs) { base_spec.metadata.push_back(ConfigParamSpec{std::string{controlOption()}, VariantType::Bool, true, {"\"\""}}); DataSpecUtils::updateInputList(inputs, std::forward(base_spec)); @@ -182,7 +170,7 @@ bool newDataframeCondition(InputRecord&, C&) template bool newDataframeCondition(InputRecord& record, C& condition) { - condition.instance = (typename C::type*)record.get(condition.path).get(); + condition.instance = (typename C::type*)record.get(condition.path).release(); return true; } @@ -289,9 +277,8 @@ bool prepareOutput(ProcessingContext& context, T& spawns) { using metadata = o2::aod::MetadataTrait>::metadata; auto originalTable = soa::ArrowHelpers::joinTables(extractOriginals(context), std::span{metadata::base_table_t::originalLabels}); - if (originalTable->schema()->fields().empty() == true) { - using base_table_t = typename T::base_table_t::table_t; - originalTable = makeEmptyTable(o2::aod::label()); + if (originalTable->num_rows() == 0) { + originalTable = makeEmptyTable(); } using D = o2::aod::Hash; @@ -308,7 +295,7 @@ template bool prepareOutput(ProcessingContext& context, T& builds) { using metadata = o2::aod::MetadataTrait>::metadata; - return builds.template build(builds.pack(), extractOriginals(context)); + return builds.build(extractOriginals(context)); } template @@ -317,9 +304,8 @@ bool prepareOutput(ProcessingContext& context, T& defines) { using metadata = o2::aod::MetadataTrait>::metadata; auto originalTable = soa::ArrowHelpers::joinTables(extractOriginals(context), std::span{metadata::base_table_t::originalLabels}); - if (originalTable->schema()->fields().empty() == true) { - using base_table_t = typename T::base_table_t::table_t; - originalTable = makeEmptyTable(o2::aod::label()); + if (originalTable->num_rows() == 0) { + originalTable = makeEmptyTable(); } if (defines.inputSchema == nullptr) { defines.inputSchema = originalTable->schema(); @@ -350,9 +336,8 @@ bool prepareDelayedOutput(ProcessingContext& context, T& defines) } using metadata = o2::aod::MetadataTrait>::metadata; auto originalTable = soa::ArrowHelpers::joinTables(extractOriginals(context), std::span{metadata::base_table_t::originalLabels}); - if (originalTable->schema()->fields().empty() == true) { - using base_table_t = typename T::base_table_t::table_t; - originalTable = makeEmptyTable(o2::aod::label()); + if (originalTable->num_rows() == 0) { + originalTable = makeEmptyTable(); } if (defines.inputSchema == nullptr) { defines.inputSchema = originalTable->schema(); @@ -549,12 +534,6 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling -template -bool preInitializeCache(InitContext&, T&) -{ - return false; -} - template bool initializeCache(ProcessingContext&, T&) { @@ -601,7 +580,7 @@ bool registerCache(T& preslice, Cache& bsks, Cache&) return true; } } - auto locate = std::find_if(bsks.begin(), bsks.end(), [&](auto const& entry) { return (entry.binding == preslice.bindingKey.binding) && (entry.key == preslice.bindingKey.key); }); + auto locate = std::find(bsks.begin(), bsks.end(), preslice.getBindingKey()); if (locate == bsks.end()) { bsks.emplace_back(preslice.getBindingKey()); } else if (locate->enabled == false) { @@ -619,7 +598,7 @@ bool registerCache(T& preslice, Cache&, Cache& bsksU) return true; } } - auto locate = std::find_if(bsksU.begin(), bsksU.end(), [&](auto const& entry) { return (entry.binding == preslice.bindingKey.binding) && (entry.key == preslice.bindingKey.key); }); + auto locate = std::find(bsksU.begin(), bsksU.end(), preslice.getBindingKey()); if (locate == bsksU.end()) { bsksU.emplace_back(preslice.getBindingKey()); } else if (locate->enabled == false) { diff --git a/Framework/Core/include/Framework/AnalysisSupportHelpers.h b/Framework/Core/include/Framework/AnalysisSupportHelpers.h index cc4d45a46c8bc..ef1d056b62f2b 100644 --- a/Framework/Core/include/Framework/AnalysisSupportHelpers.h +++ b/Framework/Core/include/Framework/AnalysisSupportHelpers.h @@ -14,7 +14,7 @@ #include "Framework/OutputSpec.h" #include "Framework/InputSpec.h" #include "Framework/DataProcessorSpec.h" -#include "Framework/AnalysisContext.h" +#include "Framework/DanglingEdgesContext.h" #include "Headers/DataHeader.h" #include @@ -39,11 +39,6 @@ struct AnalysisSupportHelpers { std::vector const& requestedSpecials, std::vector& requestedAODs, DataProcessorSpec& publisher); - static void addMissingOutputsToAnalysisCCDBFetcher(std::vector const& providedSpecials, - std::vector const& requestedSpecials, - std::vector& requestedAODs, - std::vector& requestedDYNs, - DataProcessorSpec& publisher); static void addMissingOutputsToBuilder(std::vector const& requestedSpecials, std::vector& requestedAODs, std::vector& requestedDYNs, diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 53f6bc0f862d6..fbd523c7b0c37 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -22,7 +22,6 @@ #include "Framework/EndOfStreamContext.h" #include "Framework/GroupSlicer.h" #include "Framework/StructToTuple.h" -#include "Framework/Traits.h" #include "Framework/TypeIdHelpers.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/AnalysisDataModel.h" @@ -37,6 +36,9 @@ namespace o2::framework { +/// Convert a CamelCase task struct name to snake-case task name +std::string type_to_task_name(std::string_view const& camelCase); + /// A more familiar task API for the DPL analysis framework. /// This allows you to define your own tasks as subclasses /// of o2::framework::AnalysisTask and to pass them in the specification @@ -63,28 +65,31 @@ static constexpr bool is_enumeration_v> = true; template concept is_enumeration = is_enumeration_v>; +template +concept is_table_iterator_or_enumeration = soa::is_table_or_iterator || is_enumeration; + // Helper struct which builds a DataProcessorSpec from // the contents of an AnalysisTask... namespace { struct AnalysisDataProcessorBuilder { - template + template static void addGroupingCandidates(Cache& bk, Cache& bku, bool enabled) { - [&bk, &bku, enabled](framework::pack) mutable { + [](framework::pack, Cache& bk, Cache& bku, bool enabled) { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); - ([&bk, &bku, &key, enabled]() mutable { + ([](Cache& bk, Cache& bku, bool enabled, std::string const& key) { if constexpr (soa::relatedByIndex, std::decay_t>()) { - auto binding = soa::getLabelFromTypeForKey>(key); + Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { - framework::updatePairList(bku, binding, key, enabled); + framework::updatePairList(bku, e); } else { - framework::updatePairList(bk, binding, key, enabled); + framework::updatePairList(bk, e); } } - }(), + }(bk, bku, enabled, key), ...); - }(framework::pack{}); + }(framework::pack{}, bk, bku, enabled); } template @@ -168,8 +173,8 @@ struct AnalysisDataProcessorBuilder { return true; } /// 1. enumeration (must be the only argument) - template - static void inputsFromArgs(R (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) { std::vector inputMetadata; // FIXME: for the moment we do not support begin, end and step. @@ -177,44 +182,44 @@ struct AnalysisDataProcessorBuilder { } /// 2. 1st argument is an iterator - template - static void inputsFromArgs(R (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) + template + static void inputsFromArgs(void (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) requires(std::is_lvalue_reference_v && (std::is_lvalue_reference_v && ...)) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions::parent_t, Args...>(hash, name, value, inputs, eInfos); } /// 3. generic case - template - static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) requires(std::is_lvalue_reference_v && ...) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions(hash, name, value, inputs, eInfos); } /// 1. enumeration (no grouping) - template - static void cacheFromArgs(R (C::*)(A), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A), bool, Cache&, Cache&) { } /// 2. iterator (the only grouping case) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) { addGroupingCandidates(bk, bku, value); } /// 3. generic case (no grouping) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool, Cache&, Cache&) { } template static auto extractTableFromRecord(InputRecord& record) { - auto table = record.get(o2::aod::label())->asArrowTable(); + auto table = record.get(o2::aod::matcher())->asArrowTable(); if (table->num_rows() == 0) { table = makeEmptyTable(); } @@ -282,51 +287,53 @@ struct AnalysisDataProcessorBuilder { } } - template - static auto bindGroupingTable(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindGroupingTable(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return extract, 0>(record, infos, hash); } - template - static auto bindAssociatedTables(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindAssociatedTables(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as && sizeof...(Args) > 0) { constexpr auto p = pack{}; - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return std::make_tuple(extract, has_type_at_v(p) + 1>(record, infos, hash)...); } - template + template static void overwriteInternalIndices(std::tuple& dest, std::tuple const& src) { (std::get(dest).bindInternalIndicesTo(&std::get(src)), ...); } - template - static void invokeProcess(Task& task, InputRecord& inputs, R (C::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) + template + static void invokeProcess(Task& task, InputRecord& inputs, void (Task::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) { using G = std::decay_t; auto groupingTable = AnalysisDataProcessorBuilder::bindGroupingTable(inputs, processingFunction, infos); + constexpr const int numElements = nested_brace_constructible_size>() / 10; + // set filtered tables for partitions with grouping - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::setPartition(element, groupingTable); analysis_task_parsers::bindInternalIndicesPartition(element, &groupingTable); return true; }, - task); + task); if constexpr (sizeof...(Associated) == 0) { // single argument to process - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::bindExternalIndicesPartition(element, &groupingTable); analysis_task_parsers::setGroupedCombination(element, groupingTable); return true; }, - task); + task); if constexpr (soa::is_iterator) { for (auto& element : groupingTable) { std::invoke(processingFunction, task, *element); @@ -344,7 +351,7 @@ struct AnalysisDataProcessorBuilder { // pre-bind self indices std::apply( [&task](auto&... t) mutable { - (homogeneous_apply_refs( + (homogeneous_apply_refs_sized( [&t](auto& p) { analysis_task_parsers::bindInternalIndicesPartition(p, &t); return true; @@ -356,12 +363,12 @@ struct AnalysisDataProcessorBuilder { auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable { x.bindExternalIndices(&groupingTable, &std::get>(associatedTables)...); - homogeneous_apply_refs([&x](auto& t) mutable { + homogeneous_apply_refs_sized([&x](auto& t) mutable { analysis_task_parsers::setPartition(t, x); analysis_task_parsers::bindExternalIndicesPartition(t, &x); return true; }, - task); + task); }; groupingTable.bindExternalIndices(&std::get>(associatedTables)...); @@ -373,11 +380,11 @@ struct AnalysisDataProcessorBuilder { associatedTables); // GroupedCombinations bound separately, as they should be set once for all associated tables - homogeneous_apply_refs([&groupingTable, &associatedTables](auto& t) { + homogeneous_apply_refs_sized([&groupingTable, &associatedTables](auto& t) { analysis_task_parsers::setGroupedCombination(t, groupingTable, associatedTables); return true; }, - task); + task); overwriteInternalIndices(associatedTables, associatedTables); if constexpr (soa::is_iterator>) { auto slicer = GroupSlicer(groupingTable, associatedTables, slices); @@ -391,28 +398,28 @@ struct AnalysisDataProcessorBuilder { associatedSlices); // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, slice.groupingElement(), associatedSlices); } } else { // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, groupingTable, associatedTables); } } } - template + template static void invokeProcessWithArgs(C& task, T processingFunction, G g, std::tuple& at) { std::invoke(processingFunction, task, g, std::get(at)...); @@ -520,16 +527,18 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector options; std::vector expressionInfos; + constexpr const int numElements = nested_brace_constructible_size>() / 10; + /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options, &hash](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs - homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); /// parse process functions defined by corresponding configurables if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos); } - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [name = name_str, &expressionInfos, &inputs](auto& x) mutable { // this pushes (argumentIndex, processHash, schemaPtr, nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators return AnalysisDataProcessorBuilder::requestInputsFromArgs(x, name, inputs, expressionInfos); @@ -538,39 +547,39 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) // request base tables for spawnable extended tables and indices to be built // this checks for duplications - homogeneous_apply_refs([&inputs](auto& element) { + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::requestInputs(inputs, element); }, - *task.get()); + *task.get()); // no static way to check if the task defines any processing, we can only make sure it subscribes to at least something if (inputs.empty() == true) { LOG(warn) << "Task " << name_str << " has no inputs"; } - homogeneous_apply_refs([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); + homogeneous_apply_refs_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); auto requiredServices = CommonServices::defaultServices(); auto arrowServices = CommonServices::arrowServices(); requiredServices.insert(requiredServices.end(), arrowServices.begin(), arrowServices.end()); - homogeneous_apply_refs([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); + homogeneous_apply_refs_sized([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); auto algo = AlgorithmSpec::InitCallback{[task = task, expressionInfos](InitContext& ic) mutable { Cache bindingsKeys; Cache bindingsKeysUnsorted; // add preslice declarations to slicing cache definition - homogeneous_apply_refs([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); + homogeneous_apply_refs_sized([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); auto& callbacks = ic.services().get(); auto eoscb = [task](EndOfStreamContext& eosContext) { - homogeneous_apply_refs([&eosContext](auto& element) { + homogeneous_apply_refs_sized([&eosContext](auto& element) { analysis_task_parsers::postRunService(eosContext, element); analysis_task_parsers::postRunOutput(eosContext, element); return true; }, - *task.get()); + *task.get()); eosContext.services().get().readyToQuit(QuitRequest::Me); }; @@ -582,84 +591,75 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } /// update configurables in filters and partitions - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [&ic](auto& element) -> bool { return analysis_task_parsers::updatePlaceholders(ic, element); }, *task.get()); /// create expression trees for filters gandiva trees matched to schemas and store the pointers into expressionInfos - homogeneous_apply_refs([&expressionInfos](auto& element) { + homogeneous_apply_refs_sized([&expressionInfos](auto& element) { return analysis_task_parsers::createExpressionTrees(expressionInfos, element); }, - *task.get()); + *task.get()); /// parse process functions to enable requested grouping caches - note that at this state process configurables have their final values if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::cacheFromArgs(&T::process, true, bindingsKeys, bindingsKeysUnsorted); } - homogeneous_apply_refs( - [&bindingsKeys, &bindingsKeysUnsorted](auto& x) mutable { + homogeneous_apply_refs_sized( + [&bindingsKeys, &bindingsKeysUnsorted](auto& x) { return AnalysisDataProcessorBuilder::requestCacheFromArgs(x, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); ic.services().get().setCaches(std::move(bindingsKeys)); ic.services().get().setCachesUnsorted(std::move(bindingsKeysUnsorted)); - // initialize global caches - homogeneous_apply_refs([&ic](auto& element) { - return analysis_task_parsers::preInitializeCache(ic, element); - }, - *(task.get())); return [task, expressionInfos](ProcessingContext& pc) mutable { // load the ccdb object from their cache - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); // reset partitions once per dataframe - homogeneous_apply_refs([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); + homogeneous_apply_refs_sized([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); // reset selections for the next dataframe - for (auto& info : expressionInfos) { - info.resetSelection = true; - } + std::ranges::for_each(expressionInfos, [](auto& info) { info.resetSelection = true; }); // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&pc, &slices](auto& element) { + homogeneous_apply_refs_sized([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, - *(task.get())); + *(task.get())); // initialize local caches - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); // prepare outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); // execute run() if constexpr (requires { task->run(pc); }) { task->run(pc); } // execute process() - if constexpr (requires { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); }) { + if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); } // execute optional process() - homogeneous_apply_refs( - [&pc, &expressionInfos, &task, &slices](auto& x) mutable { - if constexpr (base_of_template>) { + homogeneous_apply_refs_sized( + [&pc, &expressionInfos, &task, &slices](auto& x) { + if constexpr (is_process_configurable) { if (x.value == true) { AnalysisDataProcessorBuilder::invokeProcess(*task.get(), pc.inputs(), x.process, expressionInfos, slices); return true; } + return false; } return false; }, *task.get()); // prepare delayed outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); // finalize outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); }; }}; return { name, - // FIXME: For the moment we hardcode this. We could build - // this list from the list of methods actually implemented in the - // task itself. inputs, outputs, algo, diff --git a/Framework/Core/include/Framework/ArrowTableSlicingCache.h b/Framework/Core/include/Framework/ArrowTableSlicingCache.h index a6117ec3e01bc..073eadc22d72c 100644 --- a/Framework/Core/include/Framework/ArrowTableSlicingCache.h +++ b/Framework/Core/include/Framework/ArrowTableSlicingCache.h @@ -12,6 +12,7 @@ #ifndef ARROWTABLESLICINGCACHE_H #define ARROWTABLESLICINGCACHE_H +#include "Framework/ConcreteDataMatcher.h" #include "Framework/ServiceHandle.h" #include #include @@ -36,20 +37,28 @@ struct SliceInfoUnsortedPtr { struct Entry { std::string binding; + ConcreteDataMatcher matcher; std::string key; bool enabled; - Entry(std::string b, std::string k, bool e = true) + Entry(std::string b, ConcreteDataMatcher m, std::string k, bool e = true) : binding{b}, + matcher{m}, key{k}, enabled{e} { } + + friend bool operator==(Entry const& lhs, Entry const& rhs) + { + return (lhs.matcher == rhs.matcher) && + (lhs.key == rhs.key); + } }; using Cache = std::vector; -void updatePairList(Cache& list, std::string const& binding, std::string const& key, bool enabled); +void updatePairList(Cache& list, Entry& entry); struct ArrowTableSlicingCacheDef { constexpr static ServiceKind service_kind = ServiceKind::Global; diff --git a/Framework/Core/include/Framework/ArrowTypes.h b/Framework/Core/include/Framework/ArrowTypes.h index 6fd70113fede7..2673472a81152 100644 --- a/Framework/Core/include/Framework/ArrowTypes.h +++ b/Framework/Core/include/Framework/ArrowTypes.h @@ -11,6 +11,7 @@ #ifndef O2_FRAMEWORK_ARROWTYPES_H #define O2_FRAMEWORK_ARROWTYPES_H +#include "Framework/Traits.h" #include "arrow/type_fwd.h" #include @@ -117,5 +118,54 @@ template using arrow_array_for_t = typename arrow_array_for::type; template using value_for_t = typename arrow_array_for::value_type; + +template +using array_element_t = std::decay_t()[0])>; + +template +std::shared_ptr asArrowDataType(int list_size = 1) +{ + auto typeGenerator = [](std::shared_ptr const& type, int list_size) -> std::shared_ptr { + switch (list_size) { + case -1: + return arrow::list(type); + case 1: + return std::move(type); + default: + return arrow::fixed_size_list(type, list_size); + } + }; + + if constexpr (std::is_arithmetic_v) { + if constexpr (std::same_as) { + return typeGenerator(arrow::boolean(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::uint8(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::uint16(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::uint32(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::uint64(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::int8(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::int16(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::int32(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::int64(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::float32(), list_size); + } else if constexpr (std::same_as) { + return typeGenerator(arrow::float64(), list_size); + } + } else if constexpr (std::is_bounded_array_v) { + return asArrowDataType>(std::extent_v); + } else if constexpr (o2::framework::is_specialization_v) { + return asArrowDataType(-1); + } + return nullptr; +} } // namespace o2::soa #endif // O2_FRAMEWORK_ARROWTYPES_H diff --git a/Framework/Core/include/Framework/CommonDataProcessors.h b/Framework/Core/include/Framework/CommonDataProcessors.h index 824386c4d5921..48e240c59e5d2 100644 --- a/Framework/Core/include/Framework/CommonDataProcessors.h +++ b/Framework/Core/include/Framework/CommonDataProcessors.h @@ -37,7 +37,11 @@ struct CommonDataProcessors { /// and simply discards them. @a rateLimitingChannelConfig is the configuration /// for the rate limiting channel, if any required. static DataProcessorSpec getDummySink(std::vector const& danglingInputs, std::string rateLimitingChannelConfig); + /// @return a dummy DataProcessorSpec which requires all the passed @a InputSpec + /// and simply discards them. Rate limiting goes through the DPL driver + static DataProcessorSpec getScheduledDummySink(std::vector const& danglingInputs); static AlgorithmSpec wrapWithRateLimiting(AlgorithmSpec spec); + static AlgorithmSpec wrapWithTimesliceConsumption(AlgorithmSpec spec); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/CommonLabels.h b/Framework/Core/include/Framework/CommonLabels.h new file mode 100644 index 0000000000000..8be41a33af41d --- /dev/null +++ b/Framework/Core/include/Framework/CommonLabels.h @@ -0,0 +1,26 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FRAMEWORK_COMMONLABELS_H +#define O2_FRAMEWORK_COMMONLABELS_H + +#include "Framework/DataProcessorLabel.h" + +namespace o2::framework +{ + +// Label to disable forwarding/advertising of DomainInfoHeader (oldest possible outputs) +// When present on a DataProcessor, no DomainInfoHeader messages will be sent downstream. +const extern DataProcessorLabel suppressDomainInfoLabel; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_COMMONLABELS_H diff --git a/Framework/Core/include/Framework/CommonServices.h b/Framework/Core/include/Framework/CommonServices.h index 69f3152c0ba76..f5080fcac28db 100644 --- a/Framework/Core/include/Framework/CommonServices.h +++ b/Framework/Core/include/Framework/CommonServices.h @@ -56,7 +56,6 @@ struct CommonServices { return [](InitContext&, void* service) -> void* { return service; }; } - static ServiceSpec deviceContextSpec(); static ServiceSpec dataProcessorContextSpec(); static ServiceSpec driverClientSpec(); static ServiceSpec monitoringSpec(); diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index aa336d040d30d..09ea8b7ea6b61 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -11,7 +11,6 @@ #ifndef O2_FRAMEWORK_COMPLETIONPOLICYHELPERS_H_ #define O2_FRAMEWORK_COMPLETIONPOLICYHELPERS_H_ -#include "Framework/ChannelSpec.h" #include "Framework/CompletionPolicy.h" #include "Headers/DataHeader.h" @@ -44,10 +43,9 @@ struct CompletionPolicyHelpers { /// When any of the parts of the record have been received, consume them. static CompletionPolicy consumeWhenAny(const char* name, CompletionPolicy::Matcher matcher); -#if __has_include() /// When any of the parts which has arrived has a refcount of 1. static CompletionPolicy consumeWhenAnyZeroCount(const char* name, CompletionPolicy::Matcher matcher); -#endif + /// Default matcher applies for all devices static CompletionPolicy consumeWhenAny(CompletionPolicy::Matcher matcher = [](auto const&) -> bool { return true; }) { @@ -55,6 +53,12 @@ struct CompletionPolicyHelpers { } static CompletionPolicy consumeWhenAny(std::string matchName); + // Consume all the data captured until the oldest possible timeframe + // in input indicates that nothing else can be added to this timeslice. + // Useful in case of wildcards which multiplex multiple subspecs on the + // same input. + static CompletionPolicy consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher); + /// When any of the parts of the record have been received, consume them. static CompletionPolicy consumeWhenAnyWithAllConditions(const char* name, CompletionPolicy::Matcher matcher); /// Default matcher applies for all devices diff --git a/Framework/Core/include/Framework/ComputingQuotaEvaluator.h b/Framework/Core/include/Framework/ComputingQuotaEvaluator.h index aee2e50c90e7f..17ce9c2ba3e65 100644 --- a/Framework/Core/include/Framework/ComputingQuotaEvaluator.h +++ b/Framework/Core/include/Framework/ComputingQuotaEvaluator.h @@ -32,7 +32,7 @@ class ComputingQuotaEvaluator { public: // Maximum number of offers this evaluator can hold - static constexpr int MAX_INFLIGHT_OFFERS = 16; + static constexpr int MAX_INFLIGHT_OFFERS = 32; ComputingQuotaEvaluator(ServiceRegistryRef ref); /// @a task the task which needs some quota /// @a request the resource request the @a task needs diff --git a/Framework/Core/include/Framework/ComputingQuotaOffer.h b/Framework/Core/include/Framework/ComputingQuotaOffer.h index f457f46eef774..8d4420e7af2f5 100644 --- a/Framework/Core/include/Framework/ComputingQuotaOffer.h +++ b/Framework/Core/include/Framework/ComputingQuotaOffer.h @@ -44,6 +44,8 @@ struct ComputingQuotaOffer { int64_t memory = 0; /// How much shared memory it can allocate int64_t sharedMemory = 0; + /// How many timeslices it can process without giving back control + int64_t timeslices = 0; /// How much runtime it can use before giving back the resource /// in milliseconds. int64_t runtime = 0; @@ -68,8 +70,10 @@ struct ComputingQuotaInfo { /// Statistics on the offers consumed, expired struct ComputingQuotaStats { int64_t totalConsumedBytes = 0; + int64_t totalConsumedTimeslices = 0; int64_t totalConsumedOffers = 0; int64_t totalExpiredBytes = 0; + int64_t totalExpiredTimeslices = 0; int64_t totalExpiredOffers = 0; }; @@ -80,7 +84,7 @@ using ComputingQuotaRequest = std::function&, ComputingQuotaStats&, std::function)>; +using ComputingQuotaConsumer = std::function&, ComputingQuotaStats&, std::function)>; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/ConcreteDataMatcher.h b/Framework/Core/include/Framework/ConcreteDataMatcher.h index 247e3cd6ed8b9..bfbd2a05a8709 100644 --- a/Framework/Core/include/Framework/ConcreteDataMatcher.h +++ b/Framework/Core/include/Framework/ConcreteDataMatcher.h @@ -56,9 +56,9 @@ struct ConcreteDataMatcher { header::DataDescription description; header::DataHeader::SubSpecificationType subSpec; - ConcreteDataMatcher(header::DataOrigin origin_, - header::DataDescription description_, - header::DataHeader::SubSpecificationType subSpec_) + constexpr ConcreteDataMatcher(header::DataOrigin origin_, + header::DataDescription description_, + header::DataHeader::SubSpecificationType subSpec_) : origin(origin_), description(description_), subSpec(subSpec_) diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h index 3ef90a0eee166..0931884da1ff7 100644 --- a/Framework/Core/include/Framework/Configurable.h +++ b/Framework/Core/include/Framework/Configurable.h @@ -84,10 +84,10 @@ template using MutableConfigurable = Configurable>; template -concept is_configurable = requires(T& t) { - typename T::type; +concept is_configurable = requires(T t) { requires std::same_as; - &T::operator typename T::type; + requires std::same_as; + requires std::same_as::type, decltype(t.value)>; }; using ConfigurableAxis = Configurable, ConfigParamKind::kAxisSpec, ConfigurablePolicyConst, ConfigParamKind::kAxisSpec>>; @@ -99,19 +99,18 @@ concept is_configurable_axis = is_configurable&& T::kind == ConfigParamKind::kAxisSpec; }; -template +template struct ProcessConfigurable : Configurable { - ProcessConfigurable(R (T::*process_)(As...), std::string const& name_, bool&& value_, std::string const& help_) + ProcessConfigurable(void (T::*process_)(As...), std::string const& name_, bool&& value_, std::string const& help_) : process{process_}, Configurable(name_, std::forward(value_), help_) { } - R(T::*process) - (As...); + void (T::*process)(As...); }; template -concept is_process_configurable = is_configurable && requires(T& t) { t.process; }; +concept is_process_configurable = is_configurable && requires(T t) { t.process; }; #define PROCESS_SWITCH(_Class_, _Name_, _Help_, _Default_) \ decltype(o2::framework::ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}) do##_Name_ = o2::framework::ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}; diff --git a/Framework/Core/include/Framework/AnalysisContext.h b/Framework/Core/include/Framework/DanglingEdgesContext.h similarity index 92% rename from Framework/Core/include/Framework/AnalysisContext.h rename to Framework/Core/include/Framework/DanglingEdgesContext.h index 7d1544ed312a4..90a88974db038 100644 --- a/Framework/Core/include/Framework/AnalysisContext.h +++ b/Framework/Core/include/Framework/DanglingEdgesContext.h @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_FRAMEWORK_ANALYSISCONTEXT_H_ -#define O2_FRAMEWORK_ANALYSISCONTEXT_H_ +#ifndef O2_FRAMEWORK_DANGLINGEDGESCONTEXT_H_ +#define O2_FRAMEWORK_DANGLINGEDGESCONTEXT_H_ #include #include "Framework/InputSpec.h" @@ -32,7 +32,7 @@ struct OutputObjectInfo { // This will keep track of the inputs which have // been requested and for which we will need to inject // some source device. -struct AnalysisContext { +struct DanglingEdgesContext { std::vector requestedAODs; std::vector providedAODs; std::vector requestedDYNs; @@ -63,4 +63,4 @@ struct AnalysisContext { extern template class std::vector; extern template class std::vector; -#endif // O2_FRAMEWORK_ANALYSISCONTEXT_H_ +#endif // O2_FRAMEWORK_DANGLINGEDGESCONTEXT_H_ diff --git a/Framework/Core/include/Framework/DataAllocator.h b/Framework/Core/include/Framework/DataAllocator.h index 287513ec85845..ed9a31ca2857c 100644 --- a/Framework/Core/include/Framework/DataAllocator.h +++ b/Framework/Core/include/Framework/DataAllocator.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -29,9 +29,9 @@ #include "Headers/DataHeader.h" #include -#include #include +#include #include #include #include @@ -127,6 +127,10 @@ template concept VectorOfMessageableTypes = is_specialization_v && is_messageable::value; +template +concept ContiguousMessageablesRange = std::ranges::contiguous_range && + is_messageable::value; + /// This allocator is responsible to make sure that the messages created match /// the provided spec and that depending on how many pipelined reader we /// have, messages get created on the channel for the reader of the current @@ -296,8 +300,9 @@ class DataAllocator /// /// Supported types: /// - messageable types (trivially copyable, non-polymorphic - /// - std::vector of messageable types - /// - std::vector of pointers of messageable type + /// - contiguous_range of messageable types + /// - random_access_ranges of pointers of messageable type + /// - sized range of messageable type /// - types with ROOT dictionary and implementing the ROOT ClassDef interface /// /// Note: for many use cases, especially for the messageable types, the `make` interface @@ -308,116 +313,140 @@ class DataAllocator /// Use @a ROOTSerialized type wrapper to force ROOT serialization. Same applies to /// types which do not implement the ClassDef interface but have a dictionary. template + requires(!std::ranges::contiguous_range && is_messageable::value) + void snapshot(const Output& spec, T const& object) + { + return snapshot(spec, std::span(&object, &object + 1)); + } + + void snapshot(const Output& spec, std::string_view const& object) + { + return snapshot(spec, std::span(object.data(), object.size())); + } + + // This is for snapshotting a range of contiguous messageable types + template + requires(ContiguousMessageablesRange && !std::is_pointer_v) void snapshot(const Output& spec, T const& object) { auto& proxy = mRegistry.get().proxy(); - fair::mq::MessagePtr payloadMessage; - auto serializationType = o2::header::gSerializationMethodNone; RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); - if constexpr (is_messageable::value == true) { - // Serialize a snapshot of a trivially copyable, non-polymorphic object, - payloadMessage = proxy.createOutputMessage(routeIndex, sizeof(T)); - memcpy(payloadMessage->GetData(), &object, sizeof(T)); - - serializationType = o2::header::gSerializationMethodNone; - } else if constexpr (is_specialization_v == true || - (gsl::details::is_span::value && has_messageable_value_type::value)) { - using ElementType = typename std::remove_pointer::type; - if constexpr (is_messageable::value) { - // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements - // Note: in most cases it is better to use the `make` function und work with the provided - // reference object - constexpr auto elementSizeInBytes = sizeof(ElementType); - auto sizeInBytes = elementSizeInBytes * object.size(); - payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); - - if constexpr (std::is_pointer::value == false) { - // vector of elements - if (object.data() && sizeInBytes) { - memcpy(payloadMessage->GetData(), object.data(), sizeInBytes); - } - } else { - // serialize vector of pointers to elements - auto target = reinterpret_cast(payloadMessage->GetData()); - for (auto const& pointer : object) { - memcpy(target, pointer, elementSizeInBytes); - target += elementSizeInBytes; - } - } - - serializationType = o2::header::gSerializationMethodNone; - } else if constexpr (has_root_dictionary::value) { - return snapshot(spec, ROOTSerialized(object)); - } else { - static_assert(always_static_assert_v, - "value type of std::vector not supported by API, supported types:" - "\n - messageable tyeps (trivially copyable, non-polymorphic structures)" - "\n - pointers to those" - "\n - types with ROOT dictionary and implementing ROOT ClassDef interface"); - } - } else if constexpr (is_container::value == true && has_messageable_value_type::value == true) { - // Serialize a snapshot of a std::container of trivially copyable, non-polymorphic elements - // Note: in most cases it is better to use the `make` function und work with the provided - // reference object - constexpr auto elementSizeInBytes = sizeof(typename T::value_type); - auto sizeInBytes = elementSizeInBytes * object.size(); - payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); - - // serialize vector of pointers to elements - auto target = reinterpret_cast(payloadMessage->GetData()); - for (auto const& entry : object) { - memcpy(target, (void*)&entry, elementSizeInBytes); - target += elementSizeInBytes; - } - serializationType = o2::header::gSerializationMethodNone; - } else if constexpr (has_root_dictionary::value == true || is_specialization_v == true) { - // Serialize a snapshot of an object with root dictionary - payloadMessage = proxy.createOutputMessage(routeIndex); - payloadMessage->Rebuild(4096, {64}); - if constexpr (is_specialization_v == true) { - // Explicitely ROOT serialize a snapshot of object. - // An object wrapped into type `ROOTSerialized` is explicitely marked to be ROOT serialized - // and is expected to have a ROOT dictionary. Availability can not be checked at compile time - // for all cases. - using WrappedType = typename T::wrapped_type; - static_assert(std::is_same::value || - std::is_same::value || - std::is_void::value, - "class hint must be of type TClass or const char"); - - const TClass* cl = nullptr; - if (object.getHint() == nullptr) { - // get TClass info by wrapped type - cl = TClass::GetClass(typeid(WrappedType)); - } else if (std::is_same::value) { - // the class info has been passed directly - cl = reinterpret_cast(object.getHint()); - } else if (std::is_same::value) { - // get TClass info by optional name - cl = TClass::GetClass(reinterpret_cast(object.getHint())); - } - if (has_root_dictionary::value == false && cl == nullptr) { - if (std::is_same::value) { - throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", - reinterpret_cast(object.getHint())); - } else { - throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", - typeid(WrappedType).name()); - } - } - typename root_serializer::serializer().Serialize(*payloadMessage, &object(), cl); + using ElementType = typename std::remove_pointer::type; + // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(ElementType); + auto sizeInBytes = elementSizeInBytes * object.size(); + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // vector of elements + if (object.data() && sizeInBytes) { + memcpy(payloadMessage->GetData(), object.data(), sizeInBytes); + } + + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone); + } + + // A random access range of pointers we can serialise by storing the contens one after the other. + // On the receiving side you will have to retrieve it via a span + template + requires(std::ranges::random_access_range && is_messageable>::value && std::is_pointer_v) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + using ElementType = typename std::remove_pointer_t; + // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(ElementType); + auto sizeInBytes = elementSizeInBytes * object.size(); + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // serialize vector of pointers to elements + auto target = reinterpret_cast(payloadMessage->GetData()); + for (auto const& pointer : object) { + memcpy(target, pointer, elementSizeInBytes); + target += elementSizeInBytes; + } + + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone); + } + + // This is for a range where we can know upfront how many elements there are, + // so that we can preallocate the final size by simply multipling sizeof(T) x N elements + template + requires(!std::ranges::contiguous_range && std::ranges::sized_range && has_messageable_value_type::value) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + // Serialize a snapshot of a std::container of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(typename T::value_type); + auto sizeInBytes = elementSizeInBytes * object.size(); + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // serialize vector of pointers to elements + auto target = reinterpret_cast(payloadMessage->GetData()); + for (auto const& entry : object) { + memcpy(target, (void*)&entry, elementSizeInBytes); + target += elementSizeInBytes; + } + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone); + } + + template + requires(is_specialization_v) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + // Serialize a snapshot of an object with root dictionary + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex); + payloadMessage->Rebuild(4096, {64}); + const TClass* cl = nullptr; + // Explicitely ROOT serialize a snapshot of object. + // An object wrapped into type `ROOTSerialized` is explicitely marked to be ROOT serialized + // and is expected to have a ROOT dictionary. Availability can not be checked at compile time + // for all cases. + using WrappedType = typename T::wrapped_type; + + if (object.getHint() == nullptr) { + // get TClass info by wrapped type + cl = TClass::GetClass(typeid(WrappedType)); + } else if (std::is_same::value) { + // the class info has been passed directly + cl = reinterpret_cast(object.getHint()); + } else if (std::is_same::value) { + // get TClass info by optional name + cl = TClass::GetClass(reinterpret_cast(object.getHint())); + } + if (has_root_dictionary::value == false && cl == nullptr) { + if (std::is_same::value) { + throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", + reinterpret_cast(object.getHint())); } else { - typename root_serializer::serializer().Serialize(*payloadMessage, &object, TClass::GetClass(typeid(T))); + throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", + typeid(WrappedType).name()); } - serializationType = o2::header::gSerializationMethodROOT; - } else { - static_assert(always_static_assert_v, - "data type T not supported by API, \n specializations available for" - "\n - trivially copyable, non-polymorphic structures" - "\n - std::vector of messageable structures or pointers to those" - "\n - types with ROOT dictionary and implementing ROOT ClassDef interface"); } - addPartToContext(routeIndex, std::move(payloadMessage), spec, serializationType); + typename root_serializer::serializer().Serialize(*payloadMessage, &object(), cl); + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodROOT); + } + + template + requires(!is_messageable::value && !ContiguousMessageablesRange && has_root_dictionary::value && !is_specialization_v) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + // Serialize a snapshot of an object with root dictionary + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex); + payloadMessage->Rebuild(4096, {64}); + typename root_serializer::serializer().Serialize(*payloadMessage, &object, TClass::GetClass(typeid(T))); + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodROOT); } /// Take a snapshot of a raw data array which can be either POD or may contain a serialized diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h new file mode 100644 index 0000000000000..b7a334454bb6e --- /dev/null +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -0,0 +1,239 @@ +// 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_DATASPECVIEWS_H_ +#define O2_FRAMEWORK_DATASPECVIEWS_H_ + +#include +#include +#include "DomainInfoHeader.h" +#include "SourceInfoHeader.h" +#include "Headers/DataHeader.h" +#include + +namespace o2::framework +{ + +struct count_payloads { + // ends the pipeline, returns the container + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, count_payloads self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += header->splitPayloadParts; + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts ? header->splitPayloadParts : 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return count; + } +}; + +struct count_parts { + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, count_parts self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + auto* sih = o2::header::get(r[mi]->GetData()); + auto* dih = o2::header::get(r[mi]->GetData()); + if (!header && !sih && !dih) { + throw std::runtime_error("Header information not found"); + } + // We skip oldest possible timeframe / end of stream and not consider it + // as actual parts. + if (dih || sih) { + count += 1; + mi += 2; + } else if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += 1; + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return count; + } +}; + +struct DataRefIndices { + size_t headerIdx; + size_t payloadIdx; +}; + +struct get_pair { + size_t pairId; + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_pair self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + size_t diff = self.pairId - count; + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += header->splitPayloadParts; + if (self.pairId < count) { + return {mi, mi + 1 + diff}; + } + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts ? header->splitPayloadParts : 1; + if (self.pairId < count) { + return {mi, mi + 2 * diff + 1}; + } + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + throw std::runtime_error("Payload not found"); + } +}; + +struct get_dataref_indices { + size_t part; + size_t subPart; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_dataref_indices self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + if (self.part == count) { + return {mi, mi + 1 + self.subPart}; + } + count += 1; + mi += header->splitPayloadParts + 1; + } else { + if (self.part == count) { + return {mi, mi + 2 * self.subPart + 1}; + } + count += 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + throw std::runtime_error("Payload not found"); + } +}; + +struct get_header { + size_t id; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend fair::mq::MessagePtr& operator|(R&& r, get_header self) + { + return r[(r | get_dataref_indices{self.id, 0}).headerIdx]; + } +}; + +struct get_payload { + size_t part; + size_t subPart; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend fair::mq::MessagePtr& operator|(R&& r, get_payload self) + { + return r[(r | get_dataref_indices{self.part, self.subPart}).payloadIdx]; + } +}; + +struct get_num_payloads { + size_t id; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, get_num_payloads self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (self.id == count) { + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + return header->splitPayloadParts; + } else { + return 1; + } + } + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + count += 1; + mi += header->splitPayloadParts + 1; + } else { + count += 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return 0; + } +}; + +struct MessageSet; + +struct MessageStore { + std::span sets; + size_t inputsPerSlot = 0; +}; + +struct inputs_for_slot { + TimesliceSlot slot; + template + requires requires(R r) { std::ranges::random_access_range; } + friend std::span operator|(R&& r, inputs_for_slot self) + { + return std::span(r.sets[self.slot.index * r.inputsPerSlot]); + } +}; + +struct messages_for_input { + size_t inputIdx; + template + requires std::ranges::random_access_range + friend std::span operator|(R&& r, messages_for_input self) + { + return r[self.inputIdx].messages; + } +}; + +// FIXME: we should use special index classes in place of size_t +// FIXME: we need something to substitute a range in the store with another + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_DATASPECVIEWS_H_ diff --git a/Framework/Core/include/Framework/DataProcessingContext.h b/Framework/Core/include/Framework/DataProcessingContext.h index 9b7cbc238c942..221f7b099dc07 100644 --- a/Framework/Core/include/Framework/DataProcessingContext.h +++ b/Framework/Core/include/Framework/DataProcessingContext.h @@ -23,6 +23,12 @@ struct ServiceRegistry; struct DataAllocator; struct DataProcessorSpec; +enum struct ForwardPolicy { + AtInjection, + AtCompletionPolicySatisified, + AfterProcessing +}; + struct DataProcessorContext { DataProcessorContext(DataProcessorContext const&) = delete; DataProcessorContext() = default; @@ -122,7 +128,7 @@ struct DataProcessorContext { mutable std::vector preLoopHandles; /// Wether or not the associated DataProcessor can forward things early - bool canForwardEarly = true; + ForwardPolicy forwardPolicy = ForwardPolicy::AtInjection; bool isSink = false; bool balancingInputs = true; diff --git a/Framework/Core/include/Framework/DataProcessingDevice.h b/Framework/Core/include/Framework/DataProcessingDevice.h index 67edaa99e532b..b2281274acc87 100644 --- a/Framework/Core/include/Framework/DataProcessingDevice.h +++ b/Framework/Core/include/Framework/DataProcessingDevice.h @@ -77,7 +77,7 @@ struct DeviceConfigurationHelpers { class DataProcessingDevice : public fair::mq::Device { public: - DataProcessingDevice(RunningDeviceRef ref, ServiceRegistry&, ProcessingPolicies& policies); + DataProcessingDevice(RunningDeviceRef ref, ServiceRegistry&); void Init() final; void InitTask() final; void PreRun() final; @@ -112,7 +112,6 @@ class DataProcessingDevice : public fair::mq::Device uint64_t mBeginIterationTimestamp = 0; /// The timestamp of when the current ConditionalRun was started std::vector mPendingRegionInfos; /// A list of the region infos not yet notified. std::mutex mRegionInfoMutex; - ProcessingPolicies mProcessingPolicies; /// User policies related to data processing std::vector mHandles; /// Handles to use to schedule work. std::vector mStreams; /// Information about the task running in the associated mHandle. /// Handle to wake up the main loop from other threads diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index 7a05ddf25c46c..87aeeb8922da3 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -12,6 +12,11 @@ #define O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ #include +#include "Framework/TimesliceSlot.h" +#include "Framework/TimesliceIndex.h" +#include +#include +#include namespace o2::framework { @@ -21,6 +26,13 @@ struct ForwardChannelState; struct OutputChannelInfo; struct OutputChannelSpec; struct OutputChannelState; +struct ProcessingPolicies; +struct DeviceSpec; +struct FairMQDeviceProxy; +struct MessageSet; +struct ChannelIndex; +enum struct StreamingState; +enum struct TransitionHandlingState; /// Generic helpers for DataProcessing releated functions. struct DataProcessingHelpers { @@ -35,7 +47,20 @@ struct DataProcessingHelpers { static bool sendOldestPossibleTimeframe(ServiceRegistryRef const& ref, OutputChannelInfo const& info, OutputChannelState& state, size_t timeslice); /// Broadcast the oldest possible timeslice to all channels in output static void broadcastOldestPossibleTimeslice(ServiceRegistryRef const& ref, size_t timeslice); -}; + /// change the device StreamingState to newState + static void switchState(ServiceRegistryRef const& ref, StreamingState newState); + /// check if spec is a source devide + static bool hasOnlyGenerated(DeviceSpec const& spec); + /// starts the EoS timers and returns the new TransitionHandlingState in case as new state is requested + static TransitionHandlingState updateStateTransition(ServiceRegistryRef const& ref, ProcessingPolicies const& policies); + /// Helper to route messages for forwarding + static std::vector routeForwardedMessageSet(FairMQDeviceProxy& proxy, std::vector& currentSetOfInputs, + bool copy, bool consume); + /// Helper to route messages for forwarding + static void routeForwardedMessages(FairMQDeviceProxy& proxy, std::span& currentSetOfInputs, std::vector& forwardedParts, + bool copy, bool consume); + static void cleanForwardedMessages(std::span& currentSetOfInputs, bool consume); +}; } // namespace o2::framework #endif // O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ diff --git a/Framework/Core/include/Framework/DataProcessingStats.h b/Framework/Core/include/Framework/DataProcessingStats.h index d42f9a9d26610..a1f5c0eec5568 100644 --- a/Framework/Core/include/Framework/DataProcessingStats.h +++ b/Framework/Core/include/Framework/DataProcessingStats.h @@ -57,14 +57,21 @@ enum struct ProcessingStatsId : short { CPU_USAGE_FRACTION, ARROW_BYTES_CREATED, ARROW_BYTES_DESTROYED, + ARROW_BYTES_EXPIRED, ARROW_MESSAGES_CREATED, ARROW_MESSAGES_DESTROYED, - ARROW_BYTES_EXPIRED, + TIMESLICE_OFFER_NUMBER_CONSUMED, + TIMESLICE_NUMBER_STARTED, + TIMESLICE_NUMBER_EXPIRED, + TIMESLICE_NUMBER_DONE, RESOURCE_OFFER_EXPIRED, SHM_OFFER_BYTES_CONSUMED, RESOURCES_MISSING, RESOURCES_INSUFFICIENT, RESOURCES_SATISFACTORY, + CCDB_CACHE_HIT, + CCDB_CACHE_MISS, + CCDB_CACHE_FAILURE, AVAILABLE_MANAGED_SHM_BASE = 512, }; @@ -170,9 +177,11 @@ struct DataProcessingStats { }; void registerMetric(MetricSpec const& spec); + // Update some stats as specified by the @cmd cmd void updateStats(CommandSpec cmd); + char const* findMetricNameById(ProcessingStatsId id) const; /// This will process the queue of commands required to update the stats. /// It is meant to be called periodically by a single thread. void processCommandQueue(); diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index 012b909096317..e5a2aecea1de4 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -102,7 +102,8 @@ class DataRelayer DataRelayer(CompletionPolicy const&, std::vector const& routes, TimesliceIndex&, - ServiceRegistryRef); + ServiceRegistryRef, + int); /// This invokes the appropriate `InputRoute::danglingChecker` on every /// entry in the cache and if it returns true, it creates a new @@ -114,6 +115,9 @@ class DataRelayer using OnDropCallback = std::function&, TimesliceIndex::OldestOutputInfo info)>; + // Callback for when some messages are about to be owned by the the DataRelayer + using OnInsertionCallback = std::function&)>; + /// Prune all the pending entries in the cache. void prunePending(OnDropCallback); /// Prune the cache for a given slot @@ -135,6 +139,7 @@ class DataRelayer InputInfo const& info, size_t nMessages, size_t nPayloads = 1, + OnInsertionCallback onInsertion = nullptr, OnDropCallback onDrop = nullptr); /// This is to set the oldest possible @a timeslice this relayer can diff --git a/Framework/Core/include/Framework/DataSpecUtils.h b/Framework/Core/include/Framework/DataSpecUtils.h index 588aa30da7e08..fe322334a8edb 100644 --- a/Framework/Core/include/Framework/DataSpecUtils.h +++ b/Framework/Core/include/Framework/DataSpecUtils.h @@ -127,6 +127,9 @@ struct DataSpecUtils { /// unique way a description should be done, so we keep this outside. static std::string describe(OutputSpec const& spec); + /// Describes a ConcreteDataMatcher + static std::string describe(ConcreteDataMatcher const& matcher); + /// Provide a unique label for the input spec. Again this is outside because there /// is no standard way of doing it, so better not to pollute the API. static std::string label(InputSpec const& spec); @@ -211,6 +214,9 @@ struct DataSpecUtils { /// Create an InputSpec from metadata string static InputSpec fromMetadataString(std::string s); + /// Create a concrete data matcher from serialized string + static ConcreteDataMatcher fromString(std::string s); + /// Get the origin, if available static std::optional getOptionalOrigin(InputSpec const& spec); diff --git a/Framework/Core/include/Framework/DataSpecViews.h b/Framework/Core/include/Framework/DataSpecViews.h index 0782cefd0f632..b38866d8aa6fd 100644 --- a/Framework/Core/include/Framework/DataSpecViews.h +++ b/Framework/Core/include/Framework/DataSpecViews.h @@ -31,6 +31,39 @@ static auto filter_not_matching(auto const& provided) return std::views::filter([&provided](auto const& input) { return std::none_of(provided.begin(), provided.end(), [&input](auto const& output) { return DataSpecUtils::match(input, output); }); }); } +static auto filter_matching(auto const& provided) +{ + return std::views::filter([&provided](auto const& input) { return std::any_of(provided.begin(), provided.end(), [&input](auto const& output) { return DataSpecUtils::match(input, output); }); }); +} + +static auto filter_string_params_with(std::string match) +{ + return std::views::filter([match](auto const& param) { + return (param.type == VariantType::String) && (param.name.find(match) != std::string::npos); + }); +} + +static auto filter_string_params_starts_with(std::string match) +{ + return std::views::filter([match](auto const& param) { + return (param.type == VariantType::String) && (param.name.starts_with(match)); + }); +} + +static auto input_to_output_specs() +{ + return std::views::transform([](auto const& input) { + auto concrete = DataSpecUtils::asConcreteDataMatcher(input); + return OutputSpec{concrete.origin, concrete.description, concrete.subSpec, input.lifetime, input.metadata}; + }); +} + +static auto params_to_input_specs() +{ + return std::views::transform([](auto const& param) { + return DataSpecUtils::fromMetadataString(param.defaultValue.template get()); + }); +} } // namespace o2::framework::views // namespace o2::framework::sinks @@ -54,7 +87,7 @@ struct update_input_list { template friend Container& operator|(R&& r, update_input_list self) { - for (auto& item : r) { + for (auto const& item : r) { auto copy = item; DataSpecUtils::updateInputList(self.c, std::move(copy)); } @@ -62,6 +95,21 @@ struct update_input_list { } }; +template +struct update_output_list { + Container& c; + // ends the pipeline, returns the container + template + friend Container& operator|(R&& r, update_output_list self) + { + for (auto const& item : r) { + auto copy = item; + DataSpecUtils::updateOutputList(self.c, std::move(copy)); + } + return self.c; + } +}; + } // namespace o2::framework::sinks #endif // O2_FRAMEWORK_DATASPECVIEWS_H_ diff --git a/Framework/Core/include/Framework/DataTypes.h b/Framework/Core/include/Framework/DataTypes.h index e273a78f8d0a2..3d49d6d3c03d0 100644 --- a/Framework/Core/include/Framework/DataTypes.h +++ b/Framework/Core/include/Framework/DataTypes.h @@ -51,6 +51,8 @@ enum TrackFlags : uint32_t { OrphanTrack = 0x4, // Track has no association with any collision vertex TrackTimeAsym = 0x8, // track with an asymmetric time range TPCdEdxAlt = 0x10, // TPCSignal and tpcNClsFindableMinusPID correspond for alternative dEdx since the nominal was 0 + TPCSideA = 0x20, // TPC track has A-side clusters (if any) + TPCSideC = 0x40, // TPC track has C-side clusters (if any) // NOTE Highest 4 (29..32) bits reserved for PID hypothesis }; enum TrackFlagsRun2Enum { diff --git a/Framework/Core/include/Framework/DefaultsHelpers.h b/Framework/Core/include/Framework/DefaultsHelpers.h index 16d41d03baa7f..68e64cc42a90e 100644 --- a/Framework/Core/include/Framework/DefaultsHelpers.h +++ b/Framework/Core/include/Framework/DefaultsHelpers.h @@ -12,16 +12,24 @@ #ifndef O2_FRAMEWORK_DEFAULTHELPERS_H_ #define O2_FRAMEWORK_DEFAULTHELPERS_H_ +namespace fair::mq +{ +class ProgOptions; +} + namespace o2::framework { enum struct DeploymentMode; +struct DeviceConfig; struct DefaultsHelpers { static DeploymentMode deploymentMode(); /// @true if running online static bool onlineDeploymentMode(); /// get max number of timeslices in the queue - static unsigned int pipelineLength(); + static unsigned int pipelineLength(unsigned int minLength); + static unsigned int pipelineLength(const fair::mq::ProgOptions& options); + static unsigned int pipelineLength(const DeviceConfig& dc); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/DeviceContext.h b/Framework/Core/include/Framework/DeviceContext.h index 4593e5e819ccf..a392004c2ffbf 100644 --- a/Framework/Core/include/Framework/DeviceContext.h +++ b/Framework/Core/include/Framework/DeviceContext.h @@ -21,6 +21,7 @@ typedef struct uv_signal_s uv_signal_t; namespace o2::framework { struct ComputingQuotaStats; +struct ProcessingPolicies; /// Stucture which holds the whole runtime context /// of a running device which is not stored as @@ -33,6 +34,7 @@ struct DeviceContext { int expectedRegionCallbacks = 0; int exitTransitionTimeout = 0; int dataProcessingTimeout = 0; + ProcessingPolicies& processingPolicies; }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/DeviceMetricsInfo.h b/Framework/Core/include/Framework/DeviceMetricsInfo.h index ad143b7ef9373..05249898c65a2 100644 --- a/Framework/Core/include/Framework/DeviceMetricsInfo.h +++ b/Framework/Core/include/Framework/DeviceMetricsInfo.h @@ -154,11 +154,11 @@ struct DeviceMetricsInfo { std::vector> stringMetrics; // We do not keep so many strings as metrics as history is less relevant. std::vector> floatMetrics; std::vector> enumMetrics; - std::vector()>> intTimestamps; - std::vector()>> uint64Timestamps; - std::vector()>> floatTimestamps; - std::vector()>> stringTimestamps; - std::vector()>> enumTimestamps; + std::vector> intTimestamps; + std::vector> uint64Timestamps; + std::vector> floatTimestamps; + std::vector> stringTimestamps; + std::vector> enumTimestamps; std::vector max; std::vector min; std::vector average; diff --git a/Framework/Core/include/Framework/DriverInfo.h b/Framework/Core/include/Framework/DriverInfo.h index 41b868150e047..146602575b708 100644 --- a/Framework/Core/include/Framework/DriverInfo.h +++ b/Framework/Core/include/Framework/DriverInfo.h @@ -149,6 +149,8 @@ struct DriverInfo { std::string uniqueWorkflowId = ""; /// Metrics gathering interval unsigned short resourcesMonitoringInterval = 0; + /// Where to dump the metrics + std::string resourcesMonitoringFilename = "performanceMetrics.json"; /// Metrics gathering dump to disk interval unsigned short resourcesMonitoringDumpInterval = 0; /// Port used by the websocket control. 0 means not initialised. diff --git a/Framework/Core/include/Framework/Expressions.h b/Framework/Core/include/Framework/Expressions.h index 5a889e9ae26ec..c5f50311a7d19 100644 --- a/Framework/Core/include/Framework/Expressions.h +++ b/Framework/Core/include/Framework/Expressions.h @@ -110,6 +110,8 @@ std::string upcastTo(atype::type f); /// An expression tree node corresponding to a literal value struct LiteralNode { + using var_t = LiteralValue::stored_type; + LiteralNode() : value{-1}, type{atype::INT32} @@ -120,7 +122,12 @@ struct LiteralNode { { } - using var_t = LiteralValue::stored_type; + LiteralNode(var_t v, atype::type t) + : value{v}, + type{t} + { + } + var_t value; atype::type type = atype::NA; }; @@ -617,6 +624,12 @@ inline Node ncfg(T defaultValue, std::string path) struct Filter { Filter() = default; + Filter(std::unique_ptr&& ptr) + { + node = std::move(ptr); + (void)designateSubtrees(node.get()); + } + Filter(Node&& node_) : node{std::make_unique(std::forward(node_))} { (void)designateSubtrees(node.get()); @@ -624,7 +637,6 @@ struct Filter { Filter(Filter&& other) : node{std::forward>(other.node)} { - (void)designateSubtrees(node.get()); } Filter(std::string const& input_) : input{input_} {} @@ -700,6 +712,8 @@ std::shared_ptr createProjectorHelper(size_t nColumns, expre std::shared_ptr schema, std::vector> const& fields); +std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> const& outputFields); + template std::shared_ptr createProjectors(framework::pack, std::vector> const& fields, gandiva::SchemaPtr schema) { diff --git a/Framework/Core/include/Framework/FairMQDeviceProxy.h b/Framework/Core/include/Framework/FairMQDeviceProxy.h index ab0d094c18486..dbdade465f09c 100644 --- a/Framework/Core/include/Framework/FairMQDeviceProxy.h +++ b/Framework/Core/include/Framework/FairMQDeviceProxy.h @@ -38,7 +38,9 @@ class FairMQDeviceProxy FairMQDeviceProxy() = default; FairMQDeviceProxy(FairMQDeviceProxy const&) = delete; void bind(std::vector const& outputs, std::vector const& inputs, - std::vector const& forwards, fair::mq::Device& device); + std::vector const& forwards, + std::function bindChannelByName, + std::function newStateRequestedCallback); /// Retrieve the transport associated to a given route. [[nodiscard]] OutputRoute const& getOutputRoute(RouteIndex routeIndex) const { return mOutputs.at(routeIndex.value); } diff --git a/Framework/Core/include/Framework/GroupSlicer.h b/Framework/Core/include/Framework/GroupSlicer.h index 4cfbb8c440fd3..596e68d8cdd4c 100644 --- a/Framework/Core/include/Framework/GroupSlicer.h +++ b/Framework/Core/include/Framework/GroupSlicer.h @@ -55,7 +55,7 @@ struct GroupSlicer { { constexpr auto index = framework::has_type_at_v>(associated_pack_t{}); auto binding = o2::soa::getLabelFromTypeForKey>(mIndexColumnName); - auto bk = Entry(binding, mIndexColumnName); + auto bk = Entry(binding, o2::soa::getMatcherFromTypeForKey>(mIndexColumnName), mIndexColumnName); if constexpr (!o2::soa::is_smallgroups>) { if (table.size() == 0) { return; diff --git a/Framework/Core/include/Framework/GuiCallbackContext.h b/Framework/Core/include/Framework/GuiCallbackContext.h index 1dbb6ec30e849..5bb3148621476 100644 --- a/Framework/Core/include/Framework/GuiCallbackContext.h +++ b/Framework/Core/include/Framework/GuiCallbackContext.h @@ -23,7 +23,7 @@ namespace o2::framework { struct GuiCallbackContext; -class WSDPLHandler; +struct WSDPLHandler; struct GuiRenderer { uv_timer_t drawTimer; diff --git a/Framework/Core/include/Framework/IndexBuilderHelpers.h b/Framework/Core/include/Framework/IndexBuilderHelpers.h index d02d5cfc59b3f..30754e62a8dc3 100644 --- a/Framework/Core/include/Framework/IndexBuilderHelpers.h +++ b/Framework/Core/include/Framework/IndexBuilderHelpers.h @@ -11,23 +11,32 @@ #ifndef O2_FRAMEWORK_INDEXBUILDERHELPERS_H_ #define O2_FRAMEWORK_INDEXBUILDERHELPERS_H_ -#include "arrow/array.h" #include #include #include -#include #include -#include + +namespace o2::soa +{ +enum struct IndexKind : int { + IdxInvalid = -1, + IdxSelf = 0, + IdxSingle = 1, + IdxSlice = 2, + IdxArray = 3 +}; +} // namespace o2::soa namespace o2::framework { void cannotBuildAnArray(); +void cannotCreateIndexBuilder(); struct ChunkedArrayIterator { ChunkedArrayIterator(std::shared_ptr source); - virtual ~ChunkedArrayIterator() = default; + void reset(std::shared_ptr& source); - std::shared_ptr mSource; + std::shared_ptr mSource = nullptr; size_t mPosition = 0; int mChunk = 0; size_t mOffset = 0; @@ -35,6 +44,7 @@ struct ChunkedArrayIterator { int const* mCurrent = nullptr; int const* mLast = nullptr; size_t mFirstIndex = 0; + size_t mSourceSize = 0; std::shared_ptr getCurrentArray(); void nextChunk(); @@ -42,114 +52,72 @@ struct ChunkedArrayIterator { int valueAt(size_t pos); }; -struct SelfIndexColumnBuilder { - SelfIndexColumnBuilder(const char* name, arrow::MemoryPool* pool); - virtual ~SelfIndexColumnBuilder() = default; - - template - inline std::shared_ptr result() const - { - std::shared_ptr array; - auto status = static_cast(mBuilder.get())->Finish(&array); - if (!status.ok()) { - cannotBuildAnArray(); - } +struct SelfBuilder { + std::unique_ptr mBuilder = nullptr; + std::unique_ptr keyIndex = nullptr; + SelfBuilder(arrow::MemoryPool* pool); + void reset(std::shared_ptr); - return std::make_shared(array); - } - std::shared_ptr field() const; - template - inline bool find(int) + inline bool find(int) const { return true; } - - template - inline void fill(int idx) - { - (void)static_cast(mBuilder.get())->Append(idx); - } - - std::string mColumnName; - std::shared_ptr mArrowType; - std::unique_ptr mBuilder = nullptr; + void fill(int idx); + std::shared_ptr result() const; }; -class IndexColumnBuilder : public SelfIndexColumnBuilder, public ChunkedArrayIterator -{ - public: - IndexColumnBuilder(std::shared_ptr source, const char* name, int listSize, arrow::MemoryPool* pool); - ~IndexColumnBuilder() override = default; +struct SingleBuilder : public ChunkedArrayIterator { + std::unique_ptr mBuilder = nullptr; + SingleBuilder(std::shared_ptr source, arrow::MemoryPool* pool); + void reset(std::shared_ptr source); - template - inline std::shared_ptr result() const - { - if constexpr (std::same_as>) { - return resultMulti(); - } else if constexpr (std::same_as) { - return resultSlice(); - } else { - return resultSingle(); - } - } + bool find(int idx); + void fill(int idx); + std::shared_ptr result() const; +}; - template - inline bool find(int idx) - { - if constexpr (std::same_as>) { - return findMulti(idx); - } else if constexpr (std::same_as) { - return findSlice(idx); - } else { - return findSingle(idx); - } - } +struct SliceBuilder : public ChunkedArrayIterator { + arrow::ArrayBuilder* mValueBuilder = nullptr; + std::unique_ptr mListBuilder = nullptr; + std::shared_ptr> mValues = nullptr; + std::shared_ptr> mCounts = nullptr; + int mValuePos = 0; + SliceBuilder(std::shared_ptr source, arrow::MemoryPool* pool); + void reset(std::shared_ptr source); - template - inline void fill(int idx) - { - ++mResultSize; - if constexpr (std::same_as>) { - fillMulti(idx); - } else if constexpr (std::same_as) { - fillSlice(idx); - } else { - fillSingle(idx); - } - } + bool find(int idx); + void fill(int idx); + std::shared_ptr result() const; - private: arrow::Status preSlice(); - arrow::Status preFind(); - - bool findSingle(int idx); - bool findSlice(int idx); - bool findMulti(int idx); - - void fillSingle(int idx); - void fillSlice(int idx); - void fillMulti(int idx); - - std::shared_ptr resultSingle() const; - std::shared_ptr resultSlice() const; - std::shared_ptr resultMulti() const; +}; - int mListSize = 1; +struct ArrayBuilder : public ChunkedArrayIterator { arrow::ArrayBuilder* mValueBuilder = nullptr; + std::vector mValues; + std::vector> mIndices; std::unique_ptr mListBuilder = nullptr; + ArrayBuilder(std::shared_ptr source, arrow::MemoryPool* pool); + void reset(std::shared_ptr source); - size_t mSourceSize = 0; - size_t mResultSize = 0; + bool find(int idx); + void fill(int idx); + std::shared_ptr result() const; - std::shared_ptr> mValuesArrow = nullptr; - std::shared_ptr> mCounts = nullptr; - std::vector mValues; - std::vector> mIndices; - int mFillOffset = 0; - int mValuePos = 0; + arrow::Status preFind(); }; -std::shared_ptr makeArrowTable(const char* label, std::vector>&& columns, std::vector>&& fields); +struct IndexColumnBuilder { + std::variant builder; + size_t mResultSize = 0; + int mColumnPos = -1; + IndexColumnBuilder(soa::IndexKind kind, int pos, arrow::MemoryPool* pool, std::shared_ptr source = nullptr); + void reset(std::shared_ptr source = nullptr); + + bool find(int idx); + void fill(int idx); + std::shared_ptr result() const; +}; } // namespace o2::framework #endif // O2_FRAMEWORK_INDEXBUILDERHELPERS_H_ diff --git a/Framework/Core/include/Framework/InitContext.h b/Framework/Core/include/Framework/InitContext.h index 8e616d276748b..7f6cec3c7a160 100644 --- a/Framework/Core/include/Framework/InitContext.h +++ b/Framework/Core/include/Framework/InitContext.h @@ -16,7 +16,7 @@ namespace o2::framework { -class ServiceRegistry; +struct ServiceRegistry; class ConfigParamRegistry; // This is a utility class to reduce the amount of boilerplate when defining diff --git a/Framework/Core/include/Framework/InputRecord.h b/Framework/Core/include/Framework/InputRecord.h index 0c9f36d00c634..96963f88524be 100644 --- a/Framework/Core/include/Framework/InputRecord.h +++ b/Framework/Core/include/Framework/InputRecord.h @@ -189,6 +189,7 @@ class InputRecord }; int getPos(const char* name) const; + int getPos(ConcreteDataMatcher matcher) const; [[nodiscard]] static InputPos getPos(std::vector const& routes, ConcreteDataMatcher matcher); [[nodiscard]] static DataRef getByPos(std::vector const& routes, InputSpan const& span, int pos, int part = 0); @@ -511,6 +512,27 @@ class InputRecord return cache.idToMetadata[id]; } + template + requires(std::same_as) + decltype(auto) get(ConcreteDataMatcher matcher, int part = 0) + { + auto pos = getPos(matcher); + if (pos < 0) { + auto msg = describeAvailableInputs(); + throw runtime_error_f("InputRecord::get: no input with binding %s found. %s", DataSpecUtils::describe(matcher).c_str(), msg.c_str()); + } + return getByPos(pos, part); + } + + template + requires(std::same_as) + decltype(auto) get(ConcreteDataMatcher matcher, int part = 0) + { + auto ref = get(matcher, part); + auto data = reinterpret_cast(ref.payload); + return std::make_unique(data, DataRefUtils::getPayloadSize(ref)); + } + /// Helper method to be used to check if a given part of the InputRecord is present. [[nodiscard]] bool isValid(std::string const& s) const { diff --git a/Framework/Core/include/Framework/OutputObjHeader.h b/Framework/Core/include/Framework/OutputObjHeader.h index f1c284d564f15..801642ec6af4f 100644 --- a/Framework/Core/include/Framework/OutputObjHeader.h +++ b/Framework/Core/include/Framework/OutputObjHeader.h @@ -37,6 +37,7 @@ enum OutputObjSourceType : unsigned int { /// @brief O2 header for OutputObj metadata struct OutputObjHeader : public BaseHeader { constexpr static const uint32_t sVersion = 1; + constexpr static const uint32_t MAX_REGISTRY_NAME_SIZE = 128; constexpr static const o2::header::HeaderType sHeaderType = "OutObjMD"; constexpr static const o2::header::SerializationMethod sSerializationMethod = o2::header::gSerializationMethodNone; OutputObjHandlingPolicy mPolicy; @@ -45,7 +46,9 @@ struct OutputObjHeader : public BaseHeader { uint16_t mPipelineIndex = 0; uint16_t mPipelineSize = 1; // Name of the actual container for the object, e.g. the HistogramRegistry name - char containerName[64] = {0}; + char containerName[MAX_REGISTRY_NAME_SIZE] = {0}; + // Wether or not the container should have a name + char createContainer = false; constexpr OutputObjHeader() : BaseHeader(sizeof(OutputObjHeader), sHeaderType, sSerializationMethod, sVersion), diff --git a/Framework/Core/include/Framework/ResourcePolicyHelpers.h b/Framework/Core/include/Framework/ResourcePolicyHelpers.h index abee264d75104..17599f9afb1a7 100644 --- a/Framework/Core/include/Framework/ResourcePolicyHelpers.h +++ b/Framework/Core/include/Framework/ResourcePolicyHelpers.h @@ -22,6 +22,7 @@ namespace o2::framework struct ResourcePolicyHelpers { static ResourcePolicy trivialTask(char const* taskMatcher); static ResourcePolicy cpuBoundTask(char const* taskMatcher, int maxCPUs = 1); + static ResourcePolicy rateLimitedSharedMemoryBoundTask(char const* taskMatcher, int maxMemory, int maxTimeslices); static ResourcePolicy sharedMemoryBoundTask(char const* taskMatcher, int maxMemory); }; diff --git a/Framework/Core/include/Framework/SerializationMethods.h b/Framework/Core/include/Framework/SerializationMethods.h index 31b9d24013ab4..68c509d36905f 100644 --- a/Framework/Core/include/Framework/SerializationMethods.h +++ b/Framework/Core/include/Framework/SerializationMethods.h @@ -15,6 +15,7 @@ /// @brief Type wrappers for enfording a specific serialization method #include "Framework/TypeTraits.h" +#include namespace o2::framework { @@ -43,6 +44,9 @@ namespace o2::framework /// - or - /// ROOTSerialized(object, "classname")); template + requires(!std::is_pointer_v && (std::same_as || + std::same_as || + std::is_void_v)) class ROOTSerialized { public: @@ -50,9 +54,6 @@ class ROOTSerialized using wrapped_type = T; using hint_type = HintType; - static_assert(std::is_pointer::value == false, "wrapped type can not be a pointer"); - static_assert(std::is_pointer::value == false, "hint type can not be a pointer"); - ROOTSerialized() = delete; ROOTSerialized(wrapped_type& ref, hint_type* hint = nullptr) : mRef(ref), mHint(hint) {} @@ -67,6 +68,9 @@ class ROOTSerialized }; template + requires(!std::is_pointer_v && (std::same_as || + std::same_as || + std::is_void_v)) class CCDBSerialized { public: @@ -74,9 +78,6 @@ class CCDBSerialized using wrapped_type = T; using hint_type = HintType; - static_assert(std::is_pointer::value == false, "wrapped type can not be a pointer"); - static_assert(std::is_pointer::value == false, "hint type can not be a pointer"); - CCDBSerialized() = delete; CCDBSerialized(wrapped_type& ref, hint_type* hint = nullptr) : mRef(ref), mHint(hint) {} diff --git a/Framework/Core/include/Framework/ServiceRegistry.h b/Framework/Core/include/Framework/ServiceRegistry.h index ebafd466929ff..d6516e31be62d 100644 --- a/Framework/Core/include/Framework/ServiceRegistry.h +++ b/Framework/Core/include/Framework/ServiceRegistry.h @@ -158,7 +158,7 @@ struct ServiceRegistry { /// not bonded to a specific stream, e.g. the /// name of the data processor, its inputs and outputs, /// it's algorithm. - static Salt dataProcessorSalt(short dataProcessorId) + static Salt dataProcessorSalt(short /* dataProcessorId */) { // FIXME: old behaviour for now // return {0, dataProcessorId}; diff --git a/Framework/Core/include/Framework/ServiceRegistryRef.h b/Framework/Core/include/Framework/ServiceRegistryRef.h index 910d4e726c080..85aad6d70e93b 100644 --- a/Framework/Core/include/Framework/ServiceRegistryRef.h +++ b/Framework/Core/include/Framework/ServiceRegistryRef.h @@ -112,6 +112,8 @@ class ServiceRegistryRef mRegistry.unlock(mSalt); } + static ServiceRegistryRef *globalDeviceRef(ServiceRegistryRef *ref = nullptr); + private: ServiceRegistry& mRegistry; ServiceRegistry::Salt mSalt; diff --git a/Framework/Core/include/Framework/ServiceSpec.h b/Framework/Core/include/Framework/ServiceSpec.h index 5684889e85376..aa762b5d039e0 100644 --- a/Framework/Core/include/Framework/ServiceSpec.h +++ b/Framework/Core/include/Framework/ServiceSpec.h @@ -26,12 +26,12 @@ struct ProgOptions; namespace o2::framework { -struct InitContext; +class InitContext; struct DeviceSpec; struct ServiceRegistry; -struct ServiceRegistryRef; +class ServiceRegistryRef; struct DeviceState; -struct ProcessingContext; +class ProcessingContext; class EndOfStreamContext; struct ConfigContext; struct WorkflowSpecNode; diff --git a/Framework/Core/include/Framework/StringHelpers.h b/Framework/Core/include/Framework/StringHelpers.h index 8a2d892062f70..a2ee758435efc 100644 --- a/Framework/Core/include/Framework/StringHelpers.h +++ b/Framework/Core/include/Framework/StringHelpers.h @@ -171,7 +171,7 @@ constexpr auto get_str(const char (&str)[N]) } template -constexpr auto get_size(const char (&str)[N]) +constexpr auto get_size(const char (&)[N]) { return N; } diff --git a/Framework/Core/include/Framework/TableBuilder.h b/Framework/Core/include/Framework/TableBuilder.h index 1eb493bfd052d..845820dfe4bff 100644 --- a/Framework/Core/include/Framework/TableBuilder.h +++ b/Framework/Core/include/Framework/TableBuilder.h @@ -15,10 +15,8 @@ #include "Framework/ASoA.h" #include "Framework/StructToTuple.h" #include "Framework/RuntimeError.h" -#include "arrow/type_traits.h" // Apparently needs to be on top of the arrow includes. -#include #include #include @@ -27,6 +25,7 @@ #include #include #include +#include #include #include @@ -765,88 +764,5 @@ class TableBuilder std::shared_ptr mSchema; std::vector> mArrays; }; - -template -auto makeEmptyTable(const char* name) -{ - TableBuilder b; - [[maybe_unused]] auto writer = b.cursor(); - b.setLabel(name); - return b.finalize(); -} - -template -auto makeEmptyTable() -{ - TableBuilder b; - [[maybe_unused]] auto writer = b.cursor(typename aod::MetadataTrait>::metadata::persistent_columns_t{}); - b.setLabel(aod::label()); - return b.finalize(); -} - -template -auto makeEmptyTable(const char* name, framework::pack p) -{ - TableBuilder b; - [[maybe_unused]] auto writer = b.cursor(p); - b.setLabel(name); - return b.finalize(); -} - -std::shared_ptr spawnerHelper(std::shared_ptr const& fullTable, std::shared_ptr newSchema, size_t nColumns, - expressions::Projector* projectors, const char* name, std::shared_ptr& projector); - -/// Expression-based column generator to materialize columns -template - requires(soa::has_configurable_extension::metadata>) -auto spawner(std::shared_ptr const& fullTable, const char* name, o2::framework::expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) -{ - using placeholders_pack_t = typename o2::aod::MetadataTrait::metadata::placeholders_pack_t; - if (fullTable->num_rows() == 0) { - return makeEmptyTable(name, placeholders_pack_t{}); - } - return spawnerHelper(fullTable, schema, framework::pack_size(placeholders_pack_t{}), projectors, name, projector); -} - -template - requires(soa::has_configurable_extension::metadata>) -auto spawner(std::vector>&& tables, const char* name, o2::framework::expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) -{ - auto fullTable = soa::ArrowHelpers::joinTables(std::move(tables), std::span{o2::aod::MetadataTrait::metadata::base_table_t::originalLabels}); - return spawner(fullTable, name, projectors, projector, schema); -} - -template - requires(soa::has_extension::metadata> && !soa::has_configurable_extension::metadata>) -auto spawner(std::shared_ptr const& fullTable, const char* name, expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) -{ - using expression_pack_t = typename o2::aod::MetadataTrait::metadata::expression_pack_t; - if (fullTable->num_rows() == 0) { - return makeEmptyTable(name, expression_pack_t{}); - } - return spawnerHelper(fullTable, schema, framework::pack_size(expression_pack_t{}), projectors, name, projector); -} - -template - requires(soa::has_extension::metadata> && !soa::has_configurable_extension::metadata>) -auto spawner(std::vector>&& tables, const char* name, expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) -{ - auto fullTable = soa::ArrowHelpers::joinTables(std::move(tables), std::span{o2::aod::MetadataTrait::metadata::base_table_t::originalLabels}); - return spawner(fullTable, name, projectors, projector, schema); -} - -template -auto spawner(framework::pack, std::vector>&& tables, const char* name, expressions::Projector* projectors, std::shared_ptr& projector, std::shared_ptr const& schema) -{ - std::array labels{"original"}; - auto fullTable = soa::ArrowHelpers::joinTables(std::move(tables), std::span{labels}); - if (fullTable->num_rows() == 0) { - return makeEmptyTable(name, framework::pack{}); - } - return spawnerHelper(fullTable, schema, sizeof...(C), projectors, name, projector); -} - -template -using iterator_tuple_t = std::tuple; } // namespace o2::framework #endif // FRAMEWORK_TABLEBUILDER_H diff --git a/Framework/Core/include/Framework/Variant.h b/Framework/Core/include/Framework/Variant.h index e69ca05b91d98..7121c5fad0669 100644 --- a/Framework/Core/include/Framework/Variant.h +++ b/Framework/Core/include/Framework/Variant.h @@ -274,13 +274,24 @@ struct variant_helper> { // Allocates a new store and copies into it. static void set(void* store, std::vector value) { - new (reinterpret_cast*>(store)) std::vector{}; - *(reinterpret_cast*>(store)) = value; + auto ptr = reinterpret_cast*>(store); + new (ptr) std::vector{value}; } static std::vector const& get(const void* store) { return *(reinterpret_cast const*>(store)); } }; +template <> +struct variant_helper { + static void set(void* store, std::string* values, size_t size) + { + auto ptr = reinterpret_cast*>(store); + new (ptr) std::vector{values, values + size}; + } + + static std::string const* get(const void* store) { return (*(reinterpret_cast const*>(store))).data(); } +}; + template <> struct variant_helper { static const char* get(const void* store) { return *reinterpret_cast(store); } @@ -360,6 +371,16 @@ class Variant return variant_helper::get(&mStore); } + template + [[nodiscard]] std::string const* get() const + requires(std::same_as) + { + if (mType != VariantType::ArrayString) { + throw runtime_error_f("Variant::get: Mismatch between types %d %d.", mType, VariantType::ArrayString); + } + return variant_helper::get(&mStore); + } + template void set(T value) { diff --git a/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h b/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h index a51e3e03ffc5e..05ab71d39b0e4 100644 --- a/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h +++ b/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h @@ -36,7 +36,7 @@ boost::property_tree::ptree basicVectorToBranch(std::vector&& values) } template -boost::property_tree::ptree vectorToBranch(T* values, size_t size) +boost::property_tree::ptree vectorToBranch(T const* values, size_t size) { boost::property_tree::ptree branch; branch.put_child("values", basicVectorToBranch(values, size)); @@ -150,17 +150,17 @@ extern template boost::property_tree::ptree o2::framework::basicVectorToBranch(f extern template boost::property_tree::ptree o2::framework::basicVectorToBranch(int*, size_t); extern template boost::property_tree::ptree o2::framework::basicVectorToBranch(double*, size_t); extern template boost::property_tree::ptree o2::framework::basicVectorToBranch(bool*, size_t); -extern template boost::property_tree::ptree o2::framework::basicVectorToBranch(std::basic_string*, size_t); +extern template boost::property_tree::ptree o2::framework::basicVectorToBranch(std::basic_string const*, size_t); extern template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); extern template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); extern template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); extern template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); -extern template boost::property_tree::ptree o2::framework::vectorToBranch(float*, size_t); -extern template boost::property_tree::ptree o2::framework::vectorToBranch(int*, size_t); -extern template boost::property_tree::ptree o2::framework::vectorToBranch(double*, size_t); -extern template boost::property_tree::ptree o2::framework::vectorToBranch(bool*, size_t); -extern template boost::property_tree::ptree o2::framework::vectorToBranch(std::basic_string*, size_t); +extern template boost::property_tree::ptree o2::framework::vectorToBranch(float const*, size_t); +extern template boost::property_tree::ptree o2::framework::vectorToBranch(int const*, size_t); +extern template boost::property_tree::ptree o2::framework::vectorToBranch(double const*, size_t); +extern template boost::property_tree::ptree o2::framework::vectorToBranch(bool const*, size_t); +extern template boost::property_tree::ptree o2::framework::vectorToBranch(std::basic_string const*, size_t); extern template boost::property_tree::ptree o2::framework::labeledArrayToBranch(o2::framework::LabeledArray&& array); extern template boost::property_tree::ptree o2::framework::labeledArrayToBranch(o2::framework::LabeledArray&& array); diff --git a/Framework/Core/src/AODReaderHelpers.cxx b/Framework/Core/src/AODReaderHelpers.cxx deleted file mode 100644 index 2587b8e4ca03a..0000000000000 --- a/Framework/Core/src/AODReaderHelpers.cxx +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/AODReaderHelpers.h" -#include "Framework/AnalysisHelpers.h" -#include "Framework/AnalysisDataModelHelpers.h" -#include "Framework/DataProcessingHelpers.h" -#include "Framework/ExpressionHelpers.h" -#include "Framework/AlgorithmSpec.h" -#include "Framework/ControlService.h" -#include "Framework/CallbackService.h" -#include "Framework/EndOfStreamContext.h" -#include "Framework/DataSpecUtils.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -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 doExtractOriginal(framework::pack, ProcessingContext& pc) -{ - if constexpr (sizeof...(Ts) == 1) { - return pc.inputs().get(aod::MetadataTrait>>::metadata::tableLabel())->asArrowTable(); - } else { - return std::vector{pc.inputs().get(aod::MetadataTrait::metadata::tableLabel())->asArrowTable()...}; - } -} - -template -static inline auto extractOriginalsTuple(framework::pack, ProcessingContext& pc) -{ - return std::make_tuple(extractTypedOriginal(pc)...); -} - -template -static inline auto extractOriginalsVector(framework::pack, ProcessingContext& pc) -{ - return std::vector{extractOriginal(pc)...}; -} - -template refs> -static inline auto extractOriginals(ProcessingContext& pc) -{ - return [&](std::index_sequence) -> std::vector> { - return {pc.inputs().get(o2::aod::label())->asArrowTable()...}; - }(std::make_index_sequence()); -} -namespace -{ -template - requires(D::exclusive) -auto make_build(D metadata, InputSpec const& input, ProcessingContext& pc) -{ - using metadata_t = decltype(metadata); - using Key = typename metadata_t::Key; - using index_pack_t = typename metadata_t::index_pack_t; - constexpr auto sources = metadata_t::sources; - return o2::framework::IndexBuilder::indexBuilder(input.binding.c_str(), - extractOriginals(pc), - index_pack_t{}); -} - -template - requires(!D::exclusive) -auto make_build(D metadata, InputSpec const& input, ProcessingContext& pc) -{ - using metadata_t = decltype(metadata); - using Key = typename metadata_t::Key; - using index_pack_t = typename metadata_t::index_pack_t; - constexpr auto sources = metadata_t::sources; - return o2::framework::IndexBuilder::indexBuilder(input.binding.c_str(), - extractOriginals(pc), - index_pack_t{}); -} -} // namespace - -AlgorithmSpec AODReaderHelpers::indexBuilderCallback(std::vector& requested) -{ - return AlgorithmSpec::InitCallback{[requested](InitContext& /*ic*/) { - return [requested](ProcessingContext& pc) { - auto outputs = pc.outputs(); - // spawn tables - for (auto& input : requested) { - auto&& [origin, description, version] = DataSpecUtils::asConcreteDataMatcher(input); - if (description == header::DataDescription{"MA_RN2_EX"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run2MatchedExclusiveMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_RN2_SP"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run2MatchedSparseMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_RN3_EX"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run3MatchedExclusiveMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_RN3_SP"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run3MatchedSparseMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_BCCOL_EX"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::MatchedBCCollisionsExclusiveMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_BCCOL_SP"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::MatchedBCCollisionsSparseMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_BCCOLS_EX"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::MatchedBCCollisionsExclusiveMultiMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_BCCOLS_SP"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::MatchedBCCollisionsSparseMultiMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_RN3_BC_SP"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run3MatchedToBCSparseMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_RN3_BC_EX"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run3MatchedToBCExclusiveMetadata{}, input, pc)); - } else if (description == header::DataDescription{"MA_RN2_BC_SP"}) { - outputs.adopt(Output{origin, description, version}, make_build(o2::aod::Run2MatchedToBCSparseMetadata{}, input, pc)); - } else { - throw std::runtime_error("Not an index table"); - } - } - }; - }}; -} - -namespace -{ -template -auto make_spawn(InputSpec const& input, ProcessingContext& pc) -{ - using metadata_t = o2::aod::MetadataTrait::metadata; - constexpr auto sources = metadata_t::sources; - static std::shared_ptr projector = nullptr; - static std::shared_ptr schema = std::make_shared(o2::soa::createFieldsFromColumns(typename metadata_t::expression_pack_t{})); - static auto projectors = [](framework::pack) -> std::array - { - return {{std::move(C::Projector())...}}; - } - (typename metadata_t::expression_pack_t{}); - return o2::framework::spawner(extractOriginals(pc), input.binding.c_str(), projectors.data(), projector, schema); -} -} // namespace - -AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector& requested) -{ - return AlgorithmSpec::InitCallback{[requested](InitContext& /*ic*/) { - return [requested](ProcessingContext& pc) { - auto outputs = pc.outputs(); - // spawn tables - for (auto& input : requested) { - auto&& [origin, description, version] = DataSpecUtils::asConcreteDataMatcher(input); - if (description == header::DataDescription{"EXTRACK"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXTRACK_IU"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXTRACKCOV"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXTRACKCOV_IU"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXTRACKEXTRA"}) { - if (version == 0U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (version == 1U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (version == 2U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } - } else if (description == header::DataDescription{"EXMFTTRACK"}) { - if (version == 0U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (version == 1U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } - } else if (description == header::DataDescription{"EXMFTTRACKCOV"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXFWDTRACK"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXFWDTRACKCOV"}) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (description == header::DataDescription{"EXMCPARTICLE"}) { - if (version == 0U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } else if (version == 1U) { - outputs.adopt(Output{origin, description, version}, make_spawn>(input, pc)); - } - } else { - throw runtime_error("Not an extended table"); - } - } - }; - }}; -} - -} // namespace o2::framework::readers diff --git a/Framework/Core/src/ASoA.cxx b/Framework/Core/src/ASoA.cxx index 83ca358525f9f..1c73b257f81e4 100644 --- a/Framework/Core/src/ASoA.cxx +++ b/Framework/Core/src/ASoA.cxx @@ -62,41 +62,71 @@ SelectionVector sliceSelection(std::span const& mSelectedRows, in auto start_iterator = std::lower_bound(mSelectedRows.begin(), mSelectedRows.end(), start); auto stop_iterator = std::lower_bound(start_iterator, mSelectedRows.end(), end); SelectionVector slicedSelection{start_iterator, stop_iterator}; - std::transform(slicedSelection.begin(), slicedSelection.end(), slicedSelection.begin(), - [&start](int64_t idx) { - return idx - static_cast(start); - }); + std::ranges::transform(slicedSelection.begin(), slicedSelection.end(), slicedSelection.begin(), + [&start](int64_t idx) { + return idx - static_cast(start); + }); return slicedSelection; } -std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables) +{ + std::vector> fields; + std::vector> columns; + bool notEmpty = (tables[0]->num_rows() != 0); + std::ranges::for_each(tables, [&fields, &columns, notEmpty](auto const& t) { + std::ranges::copy(t->fields(), std::back_inserter(fields)); + if (notEmpty) { + std::ranges::copy(t->columns(), std::back_inserter(columns)); + } + }); + auto schema = std::make_shared(fields); + return arrow::Table::Make(schema, columns); +} + +namespace +{ +template + requires(std::same_as) +auto makeString(T const& str) +{ + return str.c_str(); +} +template + requires(std::same_as) +auto makeString(T const& str) +{ + return str; +} + +template +void canNotJoin(std::vector> const& tables, std::span labels) { - if (tables.size() == 1) { - return tables[0]; - } for (auto i = 0U; i < tables.size() - 1; ++i) { if (tables[i]->num_rows() != tables[i + 1]->num_rows()) { throw o2::framework::runtime_error_f("Tables %s and %s have different sizes (%d vs %d) and cannot be joined!", - labels[i], labels[i + 1], tables[i]->num_rows(), tables[i + 1]->num_rows()); + makeString(labels[i]), makeString(labels[i + 1]), tables[i]->num_rows(), tables[i + 1]->num_rows()); } } - std::vector> fields; - std::vector> columns; +} +} // namespace - for (auto& t : tables) { - auto tf = t->fields(); - std::copy(tf.begin(), tf.end(), std::back_inserter(fields)); +std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +{ + if (tables.size() == 1) { + return tables[0]; } + canNotJoin(tables, labels); + return joinTables(std::forward>>(tables)); +} - auto schema = std::make_shared(fields); - - if (tables[0]->num_rows() != 0) { - for (auto& t : tables) { - auto tc = t->columns(); - std::copy(tc.begin(), tc.end(), std::back_inserter(columns)); - } +std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +{ + if (tables.size() == 1) { + return tables[0]; } - return arrow::Table::Make(schema, columns); + canNotJoin(tables, labels); + return joinTables(std::forward>>(tables)); } std::shared_ptr ArrowHelpers::concatTables(std::vector>&& tables) @@ -105,7 +135,6 @@ std::shared_ptr ArrowHelpers::concatTables(std::vector> columns; - assert(tables.size() > 1); std::vector> resultFields = tables[0]->schema()->fields(); auto compareFields = [](std::shared_ptr const& f1, std::shared_ptr const& f2) { // Let's do this with stable sorting. @@ -135,13 +164,12 @@ std::shared_ptr ArrowHelpers::concatTables(std::vector(chunks)); } - auto result = arrow::Table::Make(std::make_shared(resultFields), columns); - return result; + return arrow::Table::Make(std::make_shared(resultFields), columns); } arrow::ChunkedArray* getIndexFromLabel(arrow::Table* table, std::string_view label) { - auto field = std::find_if(table->schema()->fields().begin(), table->schema()->fields().end(), [&](std::shared_ptr const& f) { + auto field = std::ranges::find_if(table->schema()->fields(), [&](std::shared_ptr const& f) { auto caseInsensitiveCompare = [](const std::string_view& str1, const std::string& str2) { return std::ranges::equal( str1, str2, diff --git a/Framework/Core/src/AnalysisHelpers.cxx b/Framework/Core/src/AnalysisHelpers.cxx index c0f804b47f5af..b7eac692d3859 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -8,10 +8,133 @@ // In applying this license CERN does not 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/AnalysisHelpers.h" #include "Framework/ExpressionHelpers.h" +#include "ExpressionJSONHelpers.h" +#include "IndexJSONHelpers.h" + +namespace o2::soa +{ +std::vector IndexBuilder::makeBuilders(std::vector>&& tables, std::vector const& records) +{ + std::vector builders; + builders.reserve(records.size()); + auto pool = arrow::default_memory_pool(); + builders.emplace_back(IndexKind::IdxSelf, records[0].pos, pool); + if (records[0].pos >= 0) { + std::get(builders[0].builder).keyIndex = std::make_unique(tables[0]->column(records[0].pos)); + } + + for (auto i = 1U; i < records.size(); ++i) { + builders.emplace_back(records[i].kind, records[i].pos, pool, records[i].pos >= 0 ? tables[i]->column(records[i].pos) : nullptr); + } + + return builders; +} + +void IndexBuilder::resetBuilders(std::vector& builders, std::vector>&& tables) +{ + for (auto i = 0U; i < builders.size(); ++i) { + builders[i].reset(builders[i].mColumnPos >= 0 ? tables[i]->column(builders[i].mColumnPos) : nullptr); + } + + if (builders[0].mColumnPos >= 0) { + std::get(builders[0].builder).keyIndex = std::make_unique(tables[0]->column(builders[0].mColumnPos)); + } +} + +std::shared_ptr IndexBuilder::materialize(std::vector& builders, std::vector>&& tables, std::vector const& records, std::shared_ptr const& schema, bool exclusive) +{ + auto size = tables[0]->num_rows(); + if (O2_BUILTIN_UNLIKELY(builders.empty())) { + builders = makeBuilders(std::move(tables), records); + } else { + resetBuilders(builders, std::move(tables)); + } + + for (int64_t counter = 0; counter < size; ++counter) { + int64_t idx = -1; + if (std::get(builders[0].builder).keyIndex == nullptr) { + idx = counter; + } else { + idx = std::get(builders[0].builder).keyIndex->valueAt(counter); + } + + bool found = true; + std::ranges::for_each(builders, [&idx, &found](auto& builder) { found &= builder.find(idx); }); + + if (!exclusive || found) { + builders[0].fill(counter); + std::ranges::for_each(builders.begin() + 1, builders.end(), [&idx](auto& builder) { builder.fill(idx); }); + } + } + + std::vector> arrays; + arrays.reserve(builders.size()); + std::ranges::transform(builders, std::back_inserter(arrays), [](auto& builder) { return builder.result(); }); + + return arrow::Table::Make(schema, arrays); +} +} // namespace o2::soa namespace o2::framework { +std::shared_ptr makeEmptyTableImpl(const char* name, std::shared_ptr& schema) +{ + schema = schema->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{name}})); + return arrow::Table::MakeEmpty(schema).ValueOrDie(); +} + +std::shared_ptr spawnerHelper(std::shared_ptr const& fullTable, std::shared_ptr newSchema, size_t nColumns, + expressions::Projector* projectors, const char* name, + std::shared_ptr& projector) +{ + if (projector == nullptr) { + projector = framework::expressions::createProjectorHelper(nColumns, projectors, fullTable->schema(), newSchema->fields()); + } + + return spawnerHelper(fullTable, newSchema, name, nColumns, projector); +} + +std::shared_ptr spawnerHelper(std::shared_ptr const& fullTable, std::shared_ptr newSchema, + const char* name, size_t nColumns, + std::shared_ptr const& projector) +{ + arrow::TableBatchReader reader(*fullTable); + std::shared_ptr batch; + arrow::ArrayVector v; + std::vector chunks; + chunks.resize(nColumns); + std::vector> arrays; + + while (true) { + auto s = reader.ReadNext(&batch); + if (!s.ok()) { + throw runtime_error_f("Cannot read batches from the source table to spawn %s: %s", name, s.ToString().c_str()); + } + if (batch == nullptr) { + break; + } + try { + s = projector->Evaluate(*batch, arrow::default_memory_pool(), &v); + if (!s.ok()) { + throw runtime_error_f("Cannot apply projector to the source table of %s: %s", name, s.ToString().c_str()); + } + } catch (std::exception& e) { + throw runtime_error_f("Cannot apply projector to the source table of %s: exception caught: %s", name, e.what()); + } + + for (auto i = 0U; i < nColumns; ++i) { + chunks[i].emplace_back(v.at(i)); + } + } + + arrays.reserve(nColumns); + std::ranges::transform(chunks, std::back_inserter(arrays), [](auto&& chunk) { return std::make_shared(chunk); }); + + return arrow::Table::Make(newSchema, arrays); +} + void initializePartitionCaches(std::set const& hashes, std::shared_ptr const& schema, expressions::Filter const& filter, gandiva::NodePtr& tree, gandiva::FilterPtr& gfilter) { if (tree == nullptr) { @@ -26,4 +149,56 @@ void initializePartitionCaches(std::set const& hashes, std::shared_ptr gfilter = framework::expressions::createFilter(schema, framework::expressions::makeCondition(tree)); } } + +std::string serializeProjectors(std::vector& projectors) +{ + std::stringstream osm; + ExpressionJSONHelpers::write(osm, projectors); + return osm.str(); +} + +std::string serializeSchema(std::shared_ptr schema) +{ + std::stringstream osm; + ArrowJSONHelpers::write(osm, schema); + return osm.str(); +} + +std::string serializeIndexRecords(std::vector& irs) +{ + std::stringstream osm; + IndexJSONHelpers::write(osm, irs); + return osm.str(); +} + +std::vector> extractSources(ProcessingContext& pc, std::vector const& matchers) +{ + std::vector> tables; + tables.reserve(matchers.size()); + std::ranges::transform(matchers, std::back_inserter(tables), [&pc](auto const& matcher) { return pc.inputs().get(matcher)->asArrowTable(); }); + return tables; +} + +std::shared_ptr Spawner::materialize(ProcessingContext& pc) const +{ + auto tables = extractSources(pc, matchers); + auto fullTable = soa::ArrowHelpers::joinTables(std::move(tables), std::span{labels.begin(), labels.size()}); + if (fullTable->num_rows() == 0) { + return arrow::Table::MakeEmpty(schema).ValueOrDie(); + } + + return spawnerHelper(fullTable, schema, binding.c_str(), schema->num_fields(), projector); +} + +std::shared_ptr Builder::materialize(ProcessingContext& pc) +{ + if (builders == nullptr) { + builders = std::make_shared>(); + builders->reserve(records.size()); + } + std::shared_ptr result; + auto tables = extractSources(pc, matchers); + result = o2::soa::IndexBuilder::materialize(*builders.get(), std::move(tables), records, outputSchema, exclusive); + return result; +} } // namespace o2::framework diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 7cfab22885671..7453751315626 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -11,10 +11,7 @@ #include "Framework/AnalysisSupportHelpers.h" #include "Framework/DataOutputDirector.h" -#include "Framework/OutputObjHeader.h" -#include "Framework/ControlService.h" -#include "Framework/EndOfStreamContext.h" -#include "Framework/DeviceSpec.h" +#include "Framework/DataSpecViews.h" #include "Framework/PluginManager.h" #include "Framework/ConfigContext.h" #include "WorkflowHelpers.h" @@ -28,8 +25,8 @@ namespace o2::framework std::shared_ptr AnalysisSupportHelpers::getDataOutputDirector(ConfigContext const& ctx) { auto const& options = ctx.options(); - auto const& OutputsInputs = ctx.services().get().outputsInputs; - auto const& isDangling = ctx.services().get().isDangling; + auto const& OutputsInputs = ctx.services().get().outputsInputs; + auto const& isDangling = ctx.services().get().isDangling; std::shared_ptr dod = std::make_shared(); @@ -101,7 +98,7 @@ std::shared_ptr AnalysisSupportHelpers::getDataOutputDirecto if (!keepString.empty()) { dod->reset(); std::string d("dangling"); - if (d.find(keepString) == 0) { + if (keepString.starts_with(d)) { // use the dangling outputs std::vector danglingOutputs; for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { @@ -129,30 +126,11 @@ void AnalysisSupportHelpers::addMissingOutputsToReader(std::vector c std::vector const& requestedInputs, DataProcessorSpec& publisher) { - auto matchingOutputFor = [](InputSpec const& requested) { - return [&requested](OutputSpec const& provided) { - return DataSpecUtils::match(requested, provided); - }; - }; - for (InputSpec const& requested : requestedInputs) { - auto provided = std::find_if(providedOutputs.begin(), - providedOutputs.end(), - matchingOutputFor(requested)); - - if (provided != providedOutputs.end()) { - continue; - } - - auto inList = std::find_if(publisher.outputs.begin(), - publisher.outputs.end(), - matchingOutputFor(requested)); - if (inList != publisher.outputs.end()) { - continue; - } - - auto concrete = DataSpecUtils::asConcreteDataMatcher(requested); - publisher.outputs.emplace_back(concrete.origin, concrete.description, concrete.subSpec, requested.lifetime, requested.metadata); - } + requestedInputs | + views::filter_not_matching(providedOutputs) | // filter the inputs that are already provided + views::filter_not_matching(publisher.outputs) | // filter the inputs that are already covered + views::input_to_output_specs() | + sinks::append_to{publisher.outputs}; // append them to the publisher outputs } void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector const& providedSpecials, @@ -160,25 +138,20 @@ void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector std::vector& requestedAODs, DataProcessorSpec& publisher) { - for (auto& input : requestedSpecials) { - if (std::any_of(providedSpecials.begin(), providedSpecials.end(), [&input](auto const& x) { - return DataSpecUtils::match(input, x); - })) { - continue; - } - auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - publisher.outputs.emplace_back(concrete.origin, concrete.description, concrete.subSpec); - for (auto& i : input.metadata) { - if ((i.type == VariantType::String) && (i.name.find("input:") != std::string::npos)) { - auto spec = DataSpecUtils::fromMetadataString(i.defaultValue.get()); - auto j = std::find(publisher.inputs.begin(), publisher.inputs.end(), spec); - if (j == publisher.inputs.end()) { - publisher.inputs.push_back(spec); - } - DataSpecUtils::updateInputList(requestedAODs, std::move(spec)); - } - } + requestedSpecials | + views::filter_not_matching(providedSpecials) | // filter the inputs that are already provided + views::input_to_output_specs() | + sinks::append_to{publisher.outputs}; // append them to the publisher outputs + + std::vector additionalInputs; + for (auto const& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { + input.metadata | + views::filter_string_params_with("input:") | + views::params_to_input_specs() | + sinks::update_input_list{additionalInputs}; // store into a temporary } + additionalInputs | sinks::update_input_list{requestedAODs}; // update requestedAODs + additionalInputs | sinks::update_input_list{publisher.inputs}; // update publisher inputs } void AnalysisSupportHelpers::addMissingOutputsToBuilder(std::vector const& requestedSpecials, @@ -186,53 +159,26 @@ void AnalysisSupportHelpers::addMissingOutputsToBuilder(std::vector c std::vector& requestedDYNs, DataProcessorSpec& publisher) { - for (auto& input : requestedSpecials) { - auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - publisher.outputs.emplace_back(concrete.origin, concrete.description, concrete.subSpec); - for (auto& i : input.metadata) { - if ((i.type == VariantType::String) && (i.name.find("input:") != std::string::npos)) { - auto spec = DataSpecUtils::fromMetadataString(i.defaultValue.get()); - auto j = std::find_if(publisher.inputs.begin(), publisher.inputs.end(), [&](auto x) { return x.binding == spec.binding; }); - if (j == publisher.inputs.end()) { - publisher.inputs.push_back(spec); - } - if (DataSpecUtils::partialMatch(spec, AODOrigins)) { - DataSpecUtils::updateInputList(requestedAODs, std::move(spec)); - } else if (DataSpecUtils::partialMatch(spec, header::DataOrigin{"DYN"})) { - DataSpecUtils::updateInputList(requestedDYNs, std::move(spec)); - } - } - } + requestedSpecials | + views::input_to_output_specs() | + sinks::append_to{publisher.outputs}; // append them to the publisher outputs + + std::vector additionalInputs; + for (auto const& input : requestedSpecials) { + input.metadata | + views::filter_string_params_with("input:") | + views::params_to_input_specs() | + sinks::update_input_list{additionalInputs}; // store into a temporary } -} -void AnalysisSupportHelpers::addMissingOutputsToAnalysisCCDBFetcher( - std::vector const& providedSpecials, - std::vector const& requestedSpecials, - std::vector& requestedAODs, - std::vector& requestedDYNs, - DataProcessorSpec& publisher) -{ - for (auto& input : requestedSpecials) { - auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - publisher.outputs.emplace_back(concrete.origin, concrete.description, concrete.subSpec); - // FIXME: good enough for now... - for (auto& i : input.metadata) { - if ((i.type == VariantType::String) && (i.name.find("input:") != std::string::npos)) { - auto value = i.defaultValue.get(); - auto spec = DataSpecUtils::fromMetadataString(i.defaultValue.get()); - auto j = std::find_if(publisher.inputs.begin(), publisher.inputs.end(), [&](auto x) { return x.binding == spec.binding; }); - if (j == publisher.inputs.end()) { - publisher.inputs.push_back(spec); - } - if (DataSpecUtils::partialMatch(spec, AODOrigins)) { - DataSpecUtils::updateInputList(requestedAODs, std::move(spec)); - } else if (DataSpecUtils::partialMatch(spec, header::DataOrigin{"DYN"})) { - DataSpecUtils::updateInputList(requestedDYNs, std::move(spec)); - } - } - } - } + additionalInputs | sinks::update_input_list{publisher.inputs}; // update publisher inputs + // FIXME: until we have a single list of pairs + additionalInputs | + views::partial_match_filter(AODOrigins) | + sinks::update_input_list{requestedAODs}; // update requestedAODs + additionalInputs | + views::partial_match_filter(header::DataOrigin{"DYN"}) | + sinks::update_input_list{requestedDYNs}; // update requestedDYNs } // ============================================================================= @@ -254,7 +200,7 @@ DataProcessorSpec AnalysisSupportHelpers::getOutputObjHistSink(ConfigContext con DataProcessorSpec AnalysisSupportHelpers::getGlobalAODSink(ConfigContext const& ctx) { - auto& ac = ctx.services().get(); + auto& ac = ctx.services().get(); // the command line options relevant for the writer are global // see runDataProcessing.h diff --git a/Framework/Core/src/AnalysisTask.cxx b/Framework/Core/src/AnalysisTask.cxx new file mode 100644 index 0000000000000..993c597a95f49 --- /dev/null +++ b/Framework/Core/src/AnalysisTask.cxx @@ -0,0 +1,68 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include + +namespace o2::framework +{ +/// Convert a CamelCase task struct name to snake-case task name +std::string type_to_task_name(std::string_view const& camelCase) +{ + std::string result; + result.reserve(camelCase.size() * 2 + 2); + + // The first character is always -. + result += "-"; + result += static_cast(std::tolower(camelCase[0])); + + for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) { + if (std::isupper(*it) && *(it - 1) != '-') { + result += '-'; + } + result += static_cast(std::tolower(*it)); + } + // Post-process to consolidate common ALICE abbreviations + // Process backwards to handle patterns correctly + static const struct { + std::string_view pattern; + std::string_view replacement; + } abbreviations[] = { + {"-e-m-c-a-l", "-emcal"}, + {"-e-m-c", "-emc"} + }; + + std::string consolidated; + consolidated.reserve(result.size()); + + for (int i = result.size() - 1; i >= 0;) { + bool matched = false; + + for (const auto& abbr : abbreviations) { + int startPos = i - abbr.pattern.size() + 1; + if (startPos >= 0 && result.compare(startPos, abbr.pattern.size(), abbr.pattern.data()) == 0) { + consolidated.insert(0, abbr.replacement); + i = startPos - 1; + matched = true; + break; + } + } + + if (!matched) { + consolidated.insert(0, 1, result[i]); + --i; + } + } + if (consolidated[0] == '-') { + return std::string(consolidated.data() + 1); + } + + return consolidated; +} +} // namespace o2::framework diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 94764571840f4..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -10,20 +10,16 @@ // or submit itself to any jurisdiction. #include "ArrowSupport.h" -#include "Framework/AODReaderHelpers.h" #include "Framework/ArrowContext.h" #include "Framework/ArrowTableSlicingCache.h" -#include "Framework/SliceCache.h" #include "Framework/DataProcessor.h" +#include "Framework/CommonDataProcessors.h" #include "Framework/DataProcessingStats.h" #include "Framework/ServiceRegistry.h" #include "Framework/ConfigContext.h" -#include "Framework/CommonDataProcessors.h" #include "Framework/DataSpecUtils.h" #include "Framework/DataSpecViews.h" #include "Framework/DeviceSpec.h" -#include "Framework/EndOfStreamContext.h" -#include "Framework/Tracing.h" #include "Framework/DeviceMetricsInfo.h" #include "Framework/DeviceMetricsHelper.h" #include "Framework/DeviceInfo.h" @@ -37,11 +33,11 @@ #include "Framework/ServiceRegistryRef.h" #include "Framework/ServiceRegistryHelpers.h" #include "Framework/Signpost.h" +#include "Framework/DefaultsHelpers.h" #include "CommonMessageBackendsHelpers.h" #include #include "Headers/DataHeader.h" -#include "Headers/DataHeaderHelpers.h" #include #include @@ -70,7 +66,7 @@ enum struct RateLimitingState { struct RateLimitConfig { int64_t maxMemory = 2000; - int64_t maxTimeframes = 0; + int64_t maxTimeframes = 1000; }; struct MetricIndices { @@ -82,6 +78,11 @@ struct MetricIndices { size_t shmOfferBytesConsumed = -1; size_t timeframesRead = -1; size_t timeframesConsumed = -1; + size_t timeframesExpired = -1; + // Timeslices counting + size_t timeslicesStarted = -1; + size_t timeslicesExpired = -1; + size_t timeslicesDone = -1; }; std::vector createDefaultIndices(std::vector& allDevicesMetrics) @@ -89,24 +90,166 @@ std::vector createDefaultIndices(std::vector& std::vector results; for (auto& info : allDevicesMetrics) { - MetricIndices indices; - indices.arrowBytesCreated = DeviceMetricsHelper::bookNumericMetric(info, "arrow-bytes-created"); - indices.arrowBytesDestroyed = DeviceMetricsHelper::bookNumericMetric(info, "arrow-bytes-destroyed"); - indices.arrowMessagesCreated = DeviceMetricsHelper::bookNumericMetric(info, "arrow-messages-created"); - indices.arrowMessagesDestroyed = DeviceMetricsHelper::bookNumericMetric(info, "arrow-messages-destroyed"); - indices.arrowBytesExpired = DeviceMetricsHelper::bookNumericMetric(info, "arrow-bytes-expired"); - indices.shmOfferBytesConsumed = DeviceMetricsHelper::bookNumericMetric(info, "shm-offer-bytes-consumed"); - indices.timeframesRead = DeviceMetricsHelper::bookNumericMetric(info, "df-sent"); - indices.timeframesConsumed = DeviceMetricsHelper::bookNumericMetric(info, "consumed-timeframes"); - results.push_back(indices); + results.emplace_back(MetricIndices{ + .arrowBytesCreated = DeviceMetricsHelper::bookNumericMetric(info, "arrow-bytes-created"), + .arrowBytesDestroyed = DeviceMetricsHelper::bookNumericMetric(info, "arrow-bytes-destroyed"), + .arrowMessagesCreated = DeviceMetricsHelper::bookNumericMetric(info, "arrow-messages-created"), + .arrowMessagesDestroyed = DeviceMetricsHelper::bookNumericMetric(info, "arrow-messages-destroyed"), + .arrowBytesExpired = DeviceMetricsHelper::bookNumericMetric(info, "arrow-bytes-expired"), + .shmOfferBytesConsumed = DeviceMetricsHelper::bookNumericMetric(info, "shm-offer-bytes-consumed"), + .timeframesRead = DeviceMetricsHelper::bookNumericMetric(info, "df-sent"), + .timeframesConsumed = DeviceMetricsHelper::bookNumericMetric(info, "consumed-timeframes"), + .timeframesExpired = DeviceMetricsHelper::bookNumericMetric(info, "expired-timeframes"), + .timeslicesStarted = DeviceMetricsHelper::bookNumericMetric(info, "timeslices-started"), + .timeslicesExpired = DeviceMetricsHelper::bookNumericMetric(info, "timeslices-expired"), + .timeslicesDone = DeviceMetricsHelper::bookNumericMetric(info, "timeslices-done"), + }); } return results; } -uint64_t calculateAvailableSharedMemory(ServiceRegistryRef registry) +struct ResourceState { + int64_t available; + int64_t offered = 0; + int64_t lastDeviceOffered = 0; +}; +struct ResourceStats { + int64_t enoughCount; /// How many times the resources were enough + int64_t lowCount; /// How many times the resources were not enough +}; +struct ResourceSpec { + char const* name; + char const* unit; + char const* api; /// The callback to give resources to a device + int64_t maxAvailable; /// Maximum available quantity for a resource + int64_t maxQuantum; /// Largest offer which can be given + int64_t minQuantum; /// Smallest offer which can be given + int64_t metricOfferScaleFactor; /// The scale factor between the metric accounting and offers accounting +}; + +auto offerResources(ResourceState& resourceState, + ResourceSpec const& resourceSpec, + ResourceStats& resourceStats, + std::vector const& specs, + std::vector const& infos, + DevicesManager& manager, + int64_t offerConsumedCurrentValue, + int64_t offerExpiredCurrentValue, + int64_t acquiredResourceCurrentValue, + int64_t disposedResourceCurrentValue, + size_t timestamp, + DeviceMetricsInfo& driverMetrics, + std::function& availableResourceMetric, + std::function& unusedOfferedResourceMetric, + std::function& offeredResourceMetric, + void* signpostId) -> void { - return registry.get().maxMemory; -} + O2_SIGNPOST_ID_FROM_POINTER(sid, rate_limiting, signpostId); + /// We loop over the devices, starting from where we stopped last time + /// offering the minimum offer to each one + int64_t lastCandidate = -1; + int64_t possibleOffer = resourceSpec.minQuantum; + + for (size_t di = 0; di < specs.size(); di++) { + if (resourceState.available < possibleOffer) { + if (resourceStats.lowCount == 0) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "not enough", + "We do not have enough %{public}s (%llu %{public}s) to offer %llu %{public}s. Total offerings %{bytes}llu %{string}s.", + resourceSpec.name, resourceState.available, resourceSpec.unit, + possibleOffer, resourceSpec.unit, + resourceState.offered, resourceSpec.unit); + } + resourceStats.lowCount++; + resourceStats.enoughCount = 0; + break; + } else { + if (resourceStats.enoughCount == 0) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "enough", + "We are back in a state where we enough %{public}s: %llu %{public}s", + resourceSpec.name, + resourceState.available, + resourceSpec.unit); + } + resourceStats.lowCount = 0; + resourceStats.enoughCount++; + } + size_t candidate = (resourceState.lastDeviceOffered + di) % specs.size(); + + auto& info = infos[candidate]; + // Do not bother for inactive devices + // FIXME: there is probably a race condition if the device died and we did not + // took notice yet... + if (info.active == false || info.readyToQuit) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Device %s is inactive not offering %{public}s to it.", + specs[candidate].name.c_str(), resourceSpec.name); + continue; + } + if (specs[candidate].name != "internal-dpl-aod-reader") { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Device %s is not a reader. Not offering %{public}s to it.", + specs[candidate].name.c_str(), + resourceSpec.name); + continue; + } + possibleOffer = std::min(resourceSpec.maxQuantum, resourceState.available); + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Offering %llu %{public}s out of %llu to %{public}s", + possibleOffer, resourceSpec.unit, resourceState.available, specs[candidate].id.c_str()); + manager.queueMessage(specs[candidate].id.c_str(), fmt::format(fmt::runtime(resourceSpec.api), possibleOffer).data()); + resourceState.available -= possibleOffer; + resourceState.offered += possibleOffer; + lastCandidate = candidate; + } + // We had at least a valid candidate, so + // next time we offer to the next device. + if (lastCandidate >= 0) { + resourceState.lastDeviceOffered = lastCandidate + 1; + } + + // unusedOfferedSharedMemory is the amount of memory which was offered and which we know it was + // not used so far. So we need to account for the amount which got actually read (readerBytesCreated) + // and the amount which we know was given back. + static int64_t lastResourceOfferConsumed = 0; + static int64_t lastUnusedOfferedResource = 0; + if (offerConsumedCurrentValue != lastResourceOfferConsumed) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Offer consumed so far %llu", offerConsumedCurrentValue); + lastResourceOfferConsumed = offerConsumedCurrentValue; + } + int unusedOfferedResource = (resourceState.offered - (offerExpiredCurrentValue + offerConsumedCurrentValue) / resourceSpec.metricOfferScaleFactor); + if (lastUnusedOfferedResource != unusedOfferedResource) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "unusedOfferedResource(%{public}s):%{bytes}d = offered:%{bytes}llu - (expired:%{bytes}llu + consumed:%{bytes}llu) / %lli", + resourceSpec.name, + unusedOfferedResource, resourceState.offered, + offerExpiredCurrentValue / resourceSpec.metricOfferScaleFactor, + offerConsumedCurrentValue / resourceSpec.metricOfferScaleFactor, + resourceSpec.metricOfferScaleFactor); + lastUnusedOfferedResource = unusedOfferedResource; + } + // availableSharedMemory is the amount of memory which we know is available to be offered. + // We subtract the amount which we know was already offered but it's unused and we then balance how + // much was created with how much was destroyed. + resourceState.available = resourceSpec.maxAvailable + ((disposedResourceCurrentValue - acquiredResourceCurrentValue) / resourceSpec.metricOfferScaleFactor) - unusedOfferedResource; + availableResourceMetric(driverMetrics, resourceState.available, timestamp); + unusedOfferedResourceMetric(driverMetrics, unusedOfferedResource, timestamp); + + offeredResourceMetric(driverMetrics, resourceState.offered, timestamp); +}; + +auto processTimeslices = [](size_t index, DeviceMetricsInfo& deviceMetrics, bool& changed, + int64_t& totalMetricValue, size_t& lastTimestamp) { + assert(index < deviceMetrics.metrics.size()); + changed |= deviceMetrics.changed[index]; + MetricInfo info = deviceMetrics.metrics[index]; + assert(info.storeIdx < deviceMetrics.uint64Metrics.size()); + auto& data = deviceMetrics.uint64Metrics[info.storeIdx]; + auto value = (int64_t)data[(info.pos - 1) % data.size()]; + totalMetricValue += value; + auto const& timestamps = DeviceMetricsHelper::getTimestampsStore(deviceMetrics)[info.storeIdx]; + lastTimestamp = std::max(lastTimestamp, timestamps[(info.pos - 1) % data.size()]); +}; o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() { @@ -134,18 +277,34 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() int64_t totalMessagesDestroyed = 0; int64_t totalTimeframesRead = 0; int64_t totalTimeframesConsumed = 0; + int64_t totalTimeframesExpired = 0; + int64_t totalTimeslicesStarted = 0; + int64_t totalTimeslicesDone = 0; + int64_t totalTimeslicesExpired = 0; auto &driverMetrics = sm.driverMetricsInfo; auto &allDeviceMetrics = sm.deviceMetricsInfos; auto &specs = sm.deviceSpecs; auto &infos = sm.deviceInfos; - O2_SIGNPOST_ID_FROM_POINTER(sid, rate_limiting, &sm); + + // Aggregated driver metrics for timeslice rate limiting + auto createUint64DriverMetric = [&driverMetrics](char const*name) -> auto { + return DeviceMetricsHelper::createNumericMetric(driverMetrics, name); + }; + auto createIntDriverMetric = [&driverMetrics](char const*name) -> auto { + return DeviceMetricsHelper::createNumericMetric(driverMetrics, name); + }; static auto stateMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "rate-limit-state"); static auto totalBytesCreatedMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-arrow-bytes-created"); static auto shmOfferConsumedMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-shm-offer-bytes-consumed"); + // These are really to monitor the rate limiting static auto unusedOfferedSharedMemoryMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-unused-offered-shared-memory"); + static auto unusedOfferedTimeslicesMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-unused-offered-timeslices"); static auto availableSharedMemoryMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-available-shared-memory"); + static auto availableTimeslicesMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-available-timeslices"); static auto offeredSharedMemoryMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-offered-shared-memory"); + static auto offeredTimeslicesMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-offered-timeslices"); + static auto totalBytesDestroyedMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-arrow-bytes-destroyed"); static auto totalBytesExpiredMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-arrow-bytes-expired"); static auto totalMessagesCreatedMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-arrow-messages-created"); @@ -153,6 +312,12 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() static auto totalTimeframesReadMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-timeframes-read"); static auto totalTimeframesConsumedMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-timeframes-consumed"); static auto totalTimeframesInFlyMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-timeframes-in-fly"); + + static auto totalTimeslicesStartedMetric = createUint64DriverMetric("total-timeslices-started"); + static auto totalTimeslicesExpiredMetric = createUint64DriverMetric("total-timeslices-expired"); + static auto totalTimeslicesDoneMetric = createUint64DriverMetric("total-timeslices-done"); + static auto totalTimeslicesInFlyMetric = createIntDriverMetric("total-timeslices-in-fly"); + static auto totalBytesDeltaMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "arrow-bytes-delta"); static auto changedCountMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "changed-metrics-count"); static auto totalSignalsMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "aod-reader-signals"); @@ -267,6 +432,21 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() auto const& timestamps = DeviceMetricsHelper::getTimestampsStore(deviceMetrics)[info.storeIdx]; lastTimestamp = std::max(lastTimestamp, timestamps[(info.pos - 1) % data.size()]); } + { + size_t index = indices.timeframesExpired; + assert(index < deviceMetrics.metrics.size()); + changed |= deviceMetrics.changed[index]; + MetricInfo info = deviceMetrics.metrics[index]; + assert(info.storeIdx < deviceMetrics.uint64Metrics.size()); + auto& data = deviceMetrics.uint64Metrics[info.storeIdx]; + auto value = (int64_t)data[(info.pos - 1) % data.size()]; + totalTimeframesExpired += value; + auto const& timestamps = DeviceMetricsHelper::getTimestampsStore(deviceMetrics)[info.storeIdx]; + lastTimestamp = std::max(lastTimestamp, timestamps[(info.pos - 1) % data.size()]); + } + processTimeslices(indices.timeslicesStarted, deviceMetrics, changed, totalTimeslicesStarted, lastTimestamp); + processTimeslices(indices.timeslicesExpired, deviceMetrics, changed, totalTimeslicesExpired, lastTimestamp); + processTimeslices(indices.timeslicesDone, deviceMetrics, changed, totalTimeslicesDone, lastTimestamp); } static uint64_t unchangedCount = 0; if (changed) { @@ -279,121 +459,60 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() totalTimeframesReadMetric(driverMetrics, totalTimeframesRead, timestamp); totalTimeframesConsumedMetric(driverMetrics, totalTimeframesConsumed, timestamp); totalTimeframesInFlyMetric(driverMetrics, (int)(totalTimeframesRead - totalTimeframesConsumed), timestamp); + totalTimeslicesStartedMetric(driverMetrics, totalTimeslicesStarted, timestamp); + totalTimeslicesExpiredMetric(driverMetrics, totalTimeslicesExpired, timestamp); + totalTimeslicesDoneMetric(driverMetrics, totalTimeslicesDone, timestamp); + totalTimeslicesInFlyMetric(driverMetrics, (int)(totalTimeslicesStarted - totalTimeslicesDone), timestamp); totalBytesDeltaMetric(driverMetrics, totalBytesCreated - totalBytesExpired - totalBytesDestroyed, timestamp); } else { unchangedCount++; } changedCountMetric(driverMetrics, unchangedCount, timestamp); - auto maxTimeframes = registry.get().maxTimeframes; - if (maxTimeframes && (totalTimeframesRead - totalTimeframesConsumed) > maxTimeframes) { - return; - } - struct ResourceState { - int64_t available; - int64_t offered = 0; - int64_t lastDeviceOffered = 0; + + static const ResourceSpec shmResourceSpec{ + .name = "shared memory", + .unit = "MB", + .api = "/shm-offer {}", + .maxAvailable = (int64_t)registry.get().maxMemory, + .maxQuantum = 100, + .minQuantum = 50, + .metricOfferScaleFactor = 1000000, }; - struct ResourceStats { - int64_t enoughCount; - int64_t lowCount; + static const ResourceSpec timesliceResourceSpec{ + .name = "timeslice", + .unit = "timeslices", + .api = "/timeslice-offer {}", + .maxAvailable = (int64_t)registry.get().maxTimeframes, + .maxQuantum = 1, + .minQuantum = 1, + .metricOfferScaleFactor = 1, }; - struct ResourceSpec{ - int64_t maxAvailable; - int64_t maxQuantum; - int64_t minQuantum; + static ResourceState shmResourceState{ + .available = shmResourceSpec.maxAvailable, }; - static const ResourceSpec resourceSpec{ - .maxAvailable = (int64_t)calculateAvailableSharedMemory(registry), - .maxQuantum = 100, - .minQuantum = 50, + static ResourceState timesliceResourceState{ + .available = timesliceResourceSpec.maxAvailable, }; - static ResourceState resourceState{ - .available = resourceSpec.maxAvailable, + static ResourceStats shmResourceStats{ + .enoughCount = shmResourceState.available - shmResourceSpec.minQuantum > 0 ? 1 : 0, + .lowCount = shmResourceState.available - shmResourceSpec.minQuantum > 0 ? 0 : 1 }; - static ResourceStats resourceStats{ - .enoughCount = resourceState.available - resourceSpec.minQuantum > 0 ? 1 : 0, - .lowCount = resourceState.available - resourceSpec.minQuantum > 0 ? 0 : 1 + static ResourceStats timesliceResourceStats{ + .enoughCount = shmResourceState.available - shmResourceSpec.minQuantum > 0 ? 1 : 0, + .lowCount = shmResourceState.available - shmResourceSpec.minQuantum > 0 ? 0 : 1 }; - /// We loop over the devices, starting from where we stopped last time - /// offering MIN_QUANTUM_SHARED_MEMORY of shared memory to each reader. - int64_t lastCandidate = -1; - int64_t possibleOffer = resourceSpec.minQuantum; - - for (size_t di = 0; di < specs.size(); di++) { - if (resourceState.available < possibleOffer) { - if (resourceStats.lowCount == 0) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "not enough", - "We do not have enough shared memory (%{bytes}llu MB) to offer %{bytes}llu MB. Total offerings %{bytes}llu", - resourceState.available, possibleOffer, resourceState.offered); - } - resourceStats.lowCount++; - resourceStats.enoughCount = 0; - break; - } else { - if (resourceStats.enoughCount == 0) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "enough", - "We are back in a state where we enough shared memory: %{bytes}llu MB", resourceState.available); - } - resourceStats.lowCount = 0; - resourceStats.enoughCount++; - } - size_t candidate = (resourceState.lastDeviceOffered + di) % specs.size(); - - auto& info = infos[candidate]; - // Do not bother for inactive devices - // FIXME: there is probably a race condition if the device died and we did not - // took notice yet... - if (info.active == false || info.readyToQuit) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Device %s is inactive not offering memory to it.", specs[candidate].name.c_str()); - continue; - } - if (specs[candidate].name != "internal-dpl-aod-reader") { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Device %s is not a reader. Not offering memory to it.", specs[candidate].name.c_str()); - continue; - } - possibleOffer = std::min(resourceSpec.maxQuantum, resourceState.available); - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Offering %{bytes}llu MB out of %{bytes}llu to %{public}s", - possibleOffer, resourceState.available, specs[candidate].id.c_str()); - manager.queueMessage(specs[candidate].id.c_str(), fmt::format("/shm-offer {}", possibleOffer).data()); - resourceState.available -= possibleOffer; - resourceState.offered += possibleOffer; - lastCandidate = candidate; - } - // We had at least a valid candidate, so - // next time we offer to the next device. - if (lastCandidate >= 0) { - resourceState.lastDeviceOffered = lastCandidate + 1; - } - - // unusedOfferedSharedMemory is the amount of memory which was offered and which we know it was - // not used so far. So we need to account for the amount which got actually read (readerBytesCreated) - // and the amount which we know was given back. - static int64_t lastShmOfferConsumed = 0; - static int64_t lastUnusedOfferedMemory = 0; - if (shmOfferBytesConsumed != lastShmOfferConsumed) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Offer consumed so far %{bytes}llu", shmOfferBytesConsumed); - lastShmOfferConsumed = shmOfferBytesConsumed; - } - int unusedOfferedMemory = (resourceState.offered - (totalBytesExpired + shmOfferBytesConsumed) / 1000000); - if (lastUnusedOfferedMemory != unusedOfferedMemory) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "unusedOfferedMemory:%{bytes}d = offered:%{bytes}llu - (expired:%{bytes}llu + consumed:%{bytes}llu) / 1000000", - unusedOfferedMemory, resourceState.offered, totalBytesExpired / 1000000, shmOfferBytesConsumed / 1000000); - lastUnusedOfferedMemory = unusedOfferedMemory; - } - // availableSharedMemory is the amount of memory which we know is available to be offered. - // We subtract the amount which we know was already offered but it's unused and we then balance how - // much was created with how much was destroyed. - resourceState.available = resourceSpec.maxAvailable + ((totalBytesDestroyed - totalBytesCreated) / 1000000) - unusedOfferedMemory; - availableSharedMemoryMetric(driverMetrics, resourceState.available, timestamp); - unusedOfferedSharedMemoryMetric(driverMetrics, unusedOfferedMemory, timestamp); - - offeredSharedMemoryMetric(driverMetrics, resourceState.offered, timestamp); }, + offerResources(timesliceResourceState, timesliceResourceSpec, timesliceResourceStats, + specs, infos, manager, totalTimeframesConsumed, totalTimeslicesExpired, + totalTimeslicesStarted, totalTimeslicesDone, timestamp, driverMetrics, + availableTimeslicesMetric, unusedOfferedTimeslicesMetric, offeredTimeslicesMetric, + (void*)&sm); + + offerResources(shmResourceState, shmResourceSpec, shmResourceStats, + specs, infos, manager, shmOfferBytesConsumed, totalBytesExpired, + totalBytesCreated, totalBytesDestroyed, timestamp, driverMetrics, + availableSharedMemoryMetric, unusedOfferedSharedMemoryMetric, offeredSharedMemoryMetric, + (void*)&sm); }, .postDispatching = [](ProcessingContext& ctx, void* service) { using DataHeader = o2::header::DataHeader; auto* arrow = reinterpret_cast(service); @@ -412,13 +531,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() dh->dataOrigin.str, dh->dataDescription.str); continue; } - bool forwarded = false; - for (auto const& forward : ctx.services().get().forwards) { - if (DataSpecUtils::match(forward.matcher, *dh)) { - forwarded = true; - break; - } - } + bool forwarded = std::ranges::any_of(ctx.services().get().forwards, [&dh](auto const& forward) { return DataSpecUtils::match(forward.matcher, *dh); }); if (forwarded) { O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", "Message %{public}.4s/%{public}.16s is forwarded so we are not returning its memory.", @@ -446,176 +559,136 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() if (dc.options.count("aod-memory-rate-limit") && dc.options["aod-memory-rate-limit"].defaulted() == false) { config->maxMemory = std::stoll(dc.options["aod-memory-rate-limit"].as()) / 1000000; } else { - config->maxMemory = readers * 500; + config->maxMemory = readers * 2000; } - if (dc.options.count("timeframes-rate-limit") && dc.options["timeframes-rate-limit"].as() == "readers") { - config->maxTimeframes = readers; - } else { + if (dc.options.count("timeframes-rate-limit") && dc.options["timeframes-rate-limit"].defaulted() == false) { config->maxTimeframes = std::stoll(dc.options["timeframes-rate-limit"].as()); + } else { + config->maxTimeframes = readers * DefaultsHelpers::pipelineLength(dc); } static bool once = false; // Until we guarantee this is called only once... if (!once) { O2_SIGNPOST_ID_GENERATE(sid, rate_limiting); O2_SIGNPOST_EVENT_EMIT_INFO(rate_limiting, sid, "setup", - "Rate limiting set up at %{bytes}llu MB distributed over %d readers", - config->maxMemory, readers); + "Rate limiting set up at %{bytes}llu MB and %llu timeframes distributed over %d readers", + config->maxMemory, config->maxTimeframes, readers); registry.registerService(ServiceRegistryHelpers::handleForService(config)); once = true; } }, .adjustTopology = [](WorkflowSpecNode& node, ConfigContext const& ctx) { auto& workflow = node.specs; - auto spawner = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-spawner"; }); - auto analysisCCDB = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-ccdb"; }); - auto builder = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-index-builder"; }); - auto reader = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-reader"; }); - auto writer = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-writer"; }); - auto &ac = ctx.services().get(); - ac.requestedAODs.clear(); - ac.requestedDYNs.clear(); - ac.providedDYNs.clear(); - ac.providedTIMs.clear(); - ac.requestedTIMs.clear(); - + auto spawner = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-spawner"); }); + auto analysisCCDB = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-ccdb"); }); + auto builder = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-index-builder"); }); + auto writer = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-writer"); }); + auto& dec = ctx.services().get(); + dec.requestedAODs.clear(); + dec.requestedDYNs.clear(); + dec.providedDYNs.clear(); + dec.providedTIMs.clear(); + dec.requestedTIMs.clear(); auto inputSpecLessThan = [](InputSpec const& lhs, InputSpec const& rhs) { return DataSpecUtils::describe(lhs) < DataSpecUtils::describe(rhs); }; auto outputSpecLessThan = [](OutputSpec const& lhs, OutputSpec const& rhs) { return DataSpecUtils::describe(lhs) < DataSpecUtils::describe(rhs); }; if (builder != workflow.end()) { // collect currently requested IDXs - ac.requestedIDXs.clear(); - for (auto& d : workflow) { - if (d.name == builder->name) { - continue; - } - for (auto& i : d.inputs) { - if (DataSpecUtils::partialMatch(i, header::DataOrigin{"IDX"})) { - auto copy = i; - DataSpecUtils::updateInputList(ac.requestedIDXs, std::move(copy)); - } - } + dec.requestedIDXs.clear(); + for (auto& d : workflow | views::exclude_by_name(builder->name)) { + d.inputs | + views::partial_match_filter(header::DataOrigin{"IDX"}) | + sinks::update_input_list{dec.requestedIDXs}; } // recreate inputs and outputs builder->inputs.clear(); builder->outputs.clear(); - // replace AlgorithmSpec - // FIXME: it should be made more generic, so it does not need replacement... - builder->algorithm = readers::AODReaderHelpers::indexBuilderCallback(ac.requestedIDXs); - AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.requestedIDXs, ac.requestedAODs, ac.requestedDYNs, *builder); + + // load real AlgorithmSpec before deployment + builder->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.requestedIDXs, dec.requestedAODs, dec.requestedDYNs, *builder); } if (spawner != workflow.end()) { // collect currently requested DYNs - for (auto& d : workflow) { - if (d.name == spawner->name) { - continue; - } - for (auto const& i : d.inputs) { - if (DataSpecUtils::partialMatch(i, header::DataOrigin{"DYN"})) { - auto copy = i; - DataSpecUtils::updateInputList(ac.requestedDYNs, std::move(copy)); - } - } - for (auto const& o : d.outputs) { - if (DataSpecUtils::partialMatch(o, header::DataOrigin{"DYN"})) { - ac.providedDYNs.emplace_back(o); - } - } - } - std::sort(ac.requestedDYNs.begin(), ac.requestedDYNs.end(), inputSpecLessThan); - std::sort(ac.providedDYNs.begin(), ac.providedDYNs.end(), outputSpecLessThan); - ac.spawnerInputs.clear(); - for (auto& input : ac.requestedDYNs) { - if (std::none_of(ac.providedDYNs.begin(), ac.providedDYNs.end(), [&input](auto const& x) { return DataSpecUtils::match(input, x); })) { - ac.spawnerInputs.emplace_back(input); - } + for (auto& d : workflow | views::exclude_by_name(spawner->name)) { + d.inputs | + views::partial_match_filter(header::DataOrigin{"DYN"}) | + sinks::update_input_list{dec.requestedDYNs}; + d.outputs | + views::partial_match_filter(header::DataOrigin{"DYN"}) | + sinks::append_to{dec.providedDYNs}; } + std::ranges::sort(dec.requestedDYNs, inputSpecLessThan); + std::ranges::sort(dec.providedDYNs, outputSpecLessThan); + dec.spawnerInputs.clear(); + dec.requestedDYNs | + views::filter_not_matching(dec.providedDYNs) | + sinks::append_to{dec.spawnerInputs}; // recreate inputs and outputs spawner->outputs.clear(); spawner->inputs.clear(); - // replace AlgorithmSpec - // FIXME: it should be made more generic, so it does not need replacement... - spawner->algorithm = readers::AODReaderHelpers::aodSpawnerCallback(ac.spawnerInputs); - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, *spawner); + + // load real AlgorithmSpec before deployment + spawner->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, *spawner); } if (analysisCCDB != workflow.end()) { for (auto& d : workflow | views::exclude_by_name(analysisCCDB->name)) { - d.inputs | views::partial_match_filter(header::DataOrigin{"ATIM"}) | sinks::update_input_list{ac.requestedTIMs}; - d.outputs | views::partial_match_filter(header::DataOrigin{"ATIM"}) | sinks::append_to{ac.providedTIMs}; + d.inputs | views::partial_match_filter(header::DataOrigin{"ATIM"}) | sinks::update_input_list{dec.requestedTIMs}; + d.outputs | views::partial_match_filter(header::DataOrigin{"ATIM"}) | sinks::append_to{dec.providedTIMs}; } - std::sort(ac.requestedTIMs.begin(), ac.requestedTIMs.end(), inputSpecLessThan); - std::sort(ac.providedTIMs.begin(), ac.providedTIMs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedTIMs, inputSpecLessThan); + std::ranges::sort(dec.providedTIMs, outputSpecLessThan); // Use ranges::to> in C++23... - ac.analysisCCDBInputs.clear(); - ac.requestedTIMs | views::filter_not_matching(ac.providedTIMs) | sinks::append_to{ac.analysisCCDBInputs}; + dec.analysisCCDBInputs.clear(); + dec.requestedTIMs | views::filter_not_matching(dec.providedTIMs) | sinks::append_to{dec.analysisCCDBInputs}; // recreate inputs and outputs analysisCCDB->outputs.clear(); analysisCCDB->inputs.clear(); - // replace AlgorithmSpec - // FIXME: it should be made more generic, so it does not need replacement... + // load real AlgorithmSpec before deployment // FIXME how can I make the lookup depend on DYN tables as well?? analysisCCDB->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkCCDBSupport", "AnalysisCCDBFetcherPlugin", ctx); - AnalysisSupportHelpers::addMissingOutputsToAnalysisCCDBFetcher({}, ac.analysisCCDBInputs, ac.requestedAODs, ac.requestedDYNs, *analysisCCDB); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.analysisCCDBInputs, dec.requestedAODs, dec.requestedDYNs, *analysisCCDB); } if (writer != workflow.end()) { workflow.erase(writer); } + // removing writer would invalidate the reader iterator if it was created before + auto reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); + if (reader != workflow.end()) { // If reader and/or builder were adjusted, remove unneeded outputs // update currently requested AODs for (auto& d : workflow) { - for (auto const& i : d.inputs) { - if (DataSpecUtils::partialMatch(i, AODOrigins)) { - auto copy = i; - DataSpecUtils::updateInputList(ac.requestedAODs, std::move(copy)); - } - } + d.inputs | + views::partial_match_filter(AODOrigins) | + sinks::update_input_list{dec.requestedAODs}; } // remove unmatched outputs auto o_end = std::remove_if(reader->outputs.begin(), reader->outputs.end(), [&](OutputSpec const& o) { - return !DataSpecUtils::partialMatch(o, o2::header::DataDescription{"TFNumber"}) && !DataSpecUtils::partialMatch(o, o2::header::DataDescription{"TFFilename"}) && std::none_of(ac.requestedAODs.begin(), ac.requestedAODs.end(), [&](InputSpec const& i) { return DataSpecUtils::match(i, o); }); + return !DataSpecUtils::partialMatch(o, o2::header::DataDescription{"TFNumber"}) && !DataSpecUtils::partialMatch(o, o2::header::DataDescription{"TFFilename"}) && std::none_of(dec.requestedAODs.begin(), dec.requestedAODs.end(), [&](InputSpec const& i) { return DataSpecUtils::match(i, o); }); }); reader->outputs.erase(o_end, reader->outputs.end()); if (reader->outputs.empty()) { // nothing to read workflow.erase(reader); + } else { + // load reader algorithm before deployment + auto mctracks2aod = std::find_if(workflow.begin(), workflow.end(), [](auto const& x) { return x.name == "mctracks-to-aod"; }); + if (mctracks2aod == workflow.end()) { // add normal reader algorithm only if no on-the-fly generator is injected + reader->algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx)); + } // otherwise the algorithm was set in injectServiceDevices } } + WorkflowHelpers::injectAODWriter(workflow, ctx); - - // replace writer as some outputs may have become dangling and some are now consumed - auto [outputsInputs, isDangling] = WorkflowHelpers::analyzeOutputs(workflow); - - // create DataOutputDescriptor - std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); - - // select outputs of type AOD which need to be saved - // ATTENTION: if there are dangling outputs the getGlobalAODSink - // has to be created in any case! - ac.outputsInputsAOD.clear(); - - for (auto ii = 0u; ii < outputsInputs.size(); ii++) { - if (DataSpecUtils::partialMatch(outputsInputs[ii], extendedAODOrigins)) { - auto ds = dod->getDataOutputDescriptors(outputsInputs[ii]); - if (!ds.empty() || isDangling[ii]) { - ac.outputsInputsAOD.emplace_back(outputsInputs[ii]); - } - } - } - - // file sink for any AOD output - if (!ac.outputsInputsAOD.empty()) { - // add TFNumber and TFFilename as input to the writer - ac.outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); - ac.outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); - workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(ctx)); - } // Move the dummy sink at the end, if needed for (size_t i = 0; i < workflow.size(); ++i) { if (workflow[i].name == "internal-dpl-injected-dummy-sink") { @@ -651,7 +724,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowTableSlicingCacheSpec() auto& caches = service->bindingsKeys; for (auto i = 0u; i < caches.size(); ++i) { if (caches[i].enabled && pc.inputs().getPos(caches[i].binding.c_str()) >= 0) { - auto status = service->updateCacheEntry(i, pc.inputs().get(caches[i].binding.c_str())->asArrowTable()); + auto status = service->updateCacheEntry(i, pc.inputs().get(caches[i].matcher)->asArrowTable()); if (!status.ok()) { throw runtime_error_f("Failed to update slice cache for %s/%s", caches[i].binding.c_str(), caches[i].key.c_str()); } @@ -660,7 +733,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowTableSlicingCacheSpec() auto& unsortedCaches = service->bindingsKeysUnsorted; for (auto i = 0u; i < unsortedCaches.size(); ++i) { if (unsortedCaches[i].enabled && pc.inputs().getPos(unsortedCaches[i].binding.c_str()) >= 0) { - auto status = service->updateCacheEntryUnsorted(i, pc.inputs().get(unsortedCaches[i].binding.c_str())->asArrowTable()); + auto status = service->updateCacheEntryUnsorted(i, pc.inputs().get(unsortedCaches[i].matcher)->asArrowTable()); if (!status.ok()) { throw runtime_error_f("failed to update slice cache (unsorted) for %s/%s", unsortedCaches[i].binding.c_str(), unsortedCaches[i].key.c_str()); } diff --git a/Framework/Core/src/ArrowTableSlicingCache.cxx b/Framework/Core/src/ArrowTableSlicingCache.cxx index 373c98516bb09..5162c698a1d66 100644 --- a/Framework/Core/src/ArrowTableSlicingCache.cxx +++ b/Framework/Core/src/ArrowTableSlicingCache.cxx @@ -19,12 +19,30 @@ namespace o2::framework { -void updatePairList(Cache& list, std::string const& binding, std::string const& key, bool enabled = true) +namespace { - auto locate = std::find_if(list.begin(), list.end(), [&binding, &key](auto const& entry) { return (entry.binding == binding) && (entry.key == key); }); +std::shared_ptr GetColumnByNameCI(std::shared_ptr const& table, std::string const& key) +{ + auto const& fields = table->schema()->fields(); + auto target = std::find_if(fields.begin(), fields.end(), [&key](std::shared_ptr const& field) { + return [](std::string_view const& s1, std::string_view const& s2) { + return std::ranges::equal( + s1, s2, + [](char c1, char c2) { + return std::tolower(static_cast(c1)) == std::tolower(static_cast(c2)); + }); + }(field->name(), key); + }); + return table->column(std::distance(fields.begin(), target)); +} +} // namespace + +void updatePairList(Cache& list, Entry& entry) +{ + auto locate = std::find(list.begin(), list.end(), entry); if (locate == list.end()) { - list.emplace_back(binding, key, enabled); - } else if (!locate->enabled && enabled) { + list.emplace_back(entry); + } else if (!locate->enabled && entry.enabled) { locate->enabled = true; } } @@ -92,14 +110,14 @@ arrow::Status ArrowTableSlicingCache::updateCacheEntry(int pos, std::shared_ptr< if (table->num_rows() == 0) { return arrow::Status::OK(); } - auto& [b, k, e] = bindingsKeys[pos]; + auto& [b, m, k, e] = bindingsKeys[pos]; if (!e) { throw runtime_error_f("Disabled cache %s/%s update requested", b.c_str(), k.c_str()); } validateOrder(bindingsKeys[pos], table); int maxValue = -1; - auto column = table->GetColumnByName(k); + auto column = GetColumnByNameCI(table, k); // starting from the end, find the first positive value, in a sorted column it is the largest index for (auto iChunk = column->num_chunks() - 1; iChunk >= 0; --iChunk) { @@ -151,11 +169,11 @@ arrow::Status ArrowTableSlicingCache::updateCacheEntryUnsorted(int pos, const st if (table->num_rows() == 0) { return arrow::Status::OK(); } - auto& [b, k, e] = bindingsKeysUnsorted[pos]; + auto& [b, m, k, e] = bindingsKeysUnsorted[pos]; if (!e) { throw runtime_error_f("Disabled unsorted cache %s/%s update requested", b.c_str(), k.c_str()); } - auto column = table->GetColumnByName(k); + auto column = GetColumnByNameCI(table, k); auto row = 0; for (auto iChunk = 0; iChunk < column->num_chunks(); ++iChunk) { auto chunk = static_cast>(column->chunk(iChunk)->data()); @@ -192,7 +210,7 @@ std::pair ArrowTableSlicingCache::getCachePos(const Entry& bindingKey int ArrowTableSlicingCache::getCachePosSortedFor(Entry const& bindingKey) const { - auto locate = std::find_if(bindingsKeys.begin(), bindingsKeys.end(), [&](Entry const& bk) { return (bindingKey.binding == bk.binding) && (bindingKey.key == bk.key); }); + auto locate = std::ranges::find(bindingsKeys, bindingKey); if (locate != bindingsKeys.end()) { return std::distance(bindingsKeys.begin(), locate); } @@ -201,7 +219,7 @@ int ArrowTableSlicingCache::getCachePosSortedFor(Entry const& bindingKey) const int ArrowTableSlicingCache::getCachePosUnsortedFor(Entry const& bindingKey) const { - auto locate_unsorted = std::find_if(bindingsKeysUnsorted.begin(), bindingsKeysUnsorted.end(), [&](Entry const& bk) { return (bindingKey.binding == bk.binding) && (bindingKey.key == bk.key); }); + auto locate_unsorted = std::ranges::find(bindingsKeysUnsorted, bindingKey); if (locate_unsorted != bindingsKeysUnsorted.end()) { return std::distance(bindingsKeysUnsorted.begin(), locate_unsorted); } @@ -251,10 +269,13 @@ SliceInfoUnsortedPtr ArrowTableSlicingCache::getCacheUnsortedForPos(int pos) con void ArrowTableSlicingCache::validateOrder(Entry const& bindingKey, const std::shared_ptr& input) { - auto const& [target, key, enabled] = bindingKey; - auto column = input->GetColumnByName(key); + auto const& [target, matcher, key, enabled] = bindingKey; + if (!enabled) { + return; + } + auto column = o2::framework::GetColumnByNameCI(input, key); auto array0 = static_cast>(column->chunk(0)->data()); - int32_t prev = 0; + int32_t prev; int32_t cur = array0.Value(0); int32_t lastNeg = cur < 0 ? cur : 0; int32_t lastPos = cur < 0 ? -1 : cur; diff --git a/Framework/Core/src/ChannelSpecHelpers.cxx b/Framework/Core/src/ChannelSpecHelpers.cxx index c66a1964c12a0..0578c51403b26 100644 --- a/Framework/Core/src/ChannelSpecHelpers.cxx +++ b/Framework/Core/src/ChannelSpecHelpers.cxx @@ -339,6 +339,10 @@ std::string ChannelSpecHelpers::defaultIPCFolder() if (channelPrefix) { return fmt::format("@dpl_{}_", channelPrefix); } + channelPrefix = getenv("SLURM_JOB_ID"); + if (channelPrefix) { + return fmt::format("@dpl_{}_", channelPrefix); + } return "@"; #else /// Find out a place where we can write the sockets diff --git a/Framework/Core/src/CommonDataProcessors.cxx b/Framework/Core/src/CommonDataProcessors.cxx index c2431b3ab068d..67c6314de1c34 100644 --- a/Framework/Core/src/CommonDataProcessors.cxx +++ b/Framework/Core/src/CommonDataProcessors.cxx @@ -44,6 +44,8 @@ using namespace o2::framework::data_matcher; // Special log to track callbacks we know about O2_DECLARE_DYNAMIC_LOG(callbacks); +O2_DECLARE_DYNAMIC_LOG(rate_limiting); +O2_DECLARE_DYNAMIC_LOG(quota); namespace o2::framework { @@ -211,6 +213,8 @@ DataProcessorSpec CommonDataProcessors::getDummySink(std::vector cons auto oldestPossingTimeslice = timesliceIndex.getOldestPossibleOutput().timeslice.value; auto& stats = services.get(); stats.updateStats({(int)ProcessingStatsId::CONSUMED_TIMEFRAMES, DataProcessingStats::Op::Set, (int64_t)oldestPossingTimeslice}); + stats.updateStats({(int)ProcessingStatsId::TIMESLICE_NUMBER_DONE, DataProcessingStats::Op::Set, (int64_t)oldestPossingTimeslice}); + stats.processCommandQueue(); }; callbacks.set(domainInfoUpdated); @@ -224,17 +228,90 @@ DataProcessorSpec CommonDataProcessors::getDummySink(std::vector cons .labels = {{"resilient"}}}; } +// For the cases were the driver is guaranteed to be there (e.g. in analysis) we can use a +// more sophisticated controller which can get offers for timeslices so that we can rate limit +// across multiple input devices and rate limit shared memory usage without race conditions +DataProcessorSpec CommonDataProcessors::getScheduledDummySink(std::vector const& danglingOutputInputs) +{ + return DataProcessorSpec{ + .name = "internal-dpl-injected-dummy-sink", + .inputs = danglingOutputInputs, + .algorithm = AlgorithmSpec{adaptStateful([](CallbackService& callbacks, DeviceState& deviceState, InitContext& ic) { + // We update the number of consumed timeframes based on the oldestPossingTimeslice + // this information will be aggregated in the driver which will then decide wether or not a new offer for + // a timeslice should be done and to which device + auto domainInfoUpdated = [](ServiceRegistryRef services, size_t timeslice, ChannelIndex channelIndex) { + LOGP(debug, "Domain info updated with timeslice {}", timeslice); + auto& timesliceIndex = services.get(); + auto oldestPossingTimeslice = timesliceIndex.getOldestPossibleOutput().timeslice.value; + auto& stats = services.get(); + O2_SIGNPOST_ID_GENERATE(sid, rate_limiting); + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "run", "Consumed timeframes (domain info updated) to be set to %zu.", oldestPossingTimeslice); + stats.updateStats({(int)ProcessingStatsId::CONSUMED_TIMEFRAMES, DataProcessingStats::Op::Set, (int64_t)oldestPossingTimeslice}); + stats.updateStats({(int)ProcessingStatsId::TIMESLICE_NUMBER_DONE, DataProcessingStats::Op::Set, (int64_t)oldestPossingTimeslice}); + stats.processCommandQueue(); + }; + callbacks.set(domainInfoUpdated); + + return adaptStateless([](DataProcessingStats& stats, TimesliceIndex& timesliceIndex) { + O2_SIGNPOST_ID_GENERATE(sid, rate_limiting); + auto oldestPossingTimeslice = timesliceIndex.getOldestPossibleOutput().timeslice.value; + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "run", "Consumed timeframes (processing) to be set to %zu.", oldestPossingTimeslice); + stats.updateStats({(int)ProcessingStatsId::CONSUMED_TIMEFRAMES, DataProcessingStats::Op::Set, (int64_t)oldestPossingTimeslice}); + stats.updateStats({(int)ProcessingStatsId::TIMESLICE_NUMBER_DONE, DataProcessingStats::Op::Set, (int64_t)oldestPossingTimeslice}); + stats.processCommandQueue(); + }); + })}, + .labels = {{"resilient"}}}; +} + AlgorithmSpec CommonDataProcessors::wrapWithRateLimiting(AlgorithmSpec spec) { return PluginManager::wrapAlgorithm(spec, [](AlgorithmSpec::ProcessCallback& original, ProcessingContext& pcx) -> void { auto& raw = pcx.services().get(); static RateLimiter limiter; + O2_SIGNPOST_ID_FROM_POINTER(sid, rate_limiting, &pcx); auto limit = std::stoi(raw.device()->fConfig->GetValue("timeframes-rate-limit")); - LOG(detail) << "Rate limiting to " << limit << " timeframes in flight"; + O2_SIGNPOST_EVENT_EMIT_DETAIL(rate_limiting, sid, "rate limiting callback", + "Rate limiting to %d timeframes in flight", limit); limiter.check(pcx, limit, 2000); - LOG(detail) << "Rate limiting passed. Invoking old callback"; + O2_SIGNPOST_EVENT_EMIT_DETAIL(rate_limiting, sid, "rate limiting callback", + "Rate limiting passed. Invoking old callback."); + original(pcx); + O2_SIGNPOST_EVENT_EMIT_DETAIL(rate_limiting, sid, "rate limiting callback", + "Rate limited callback done."); + }); +} + +// The wrapped algorithm consumes 1 timeslice every time is invoked +AlgorithmSpec CommonDataProcessors::wrapWithTimesliceConsumption(AlgorithmSpec spec) +{ + return PluginManager::wrapAlgorithm(spec, [](AlgorithmSpec::ProcessCallback& original, ProcessingContext& pcx) -> void { original(pcx); - LOG(detail) << "Rate limited callback done"; + + auto disposeResources = [](int taskId, + std::array& offers, + ComputingQuotaStats& stats, + std::function accountDisposed) { + ComputingQuotaOffer disposed; + disposed.sharedMemory = 0; + // When invoked, we have processed one timeslice by construction. + int64_t timeslicesProcessed = 1; + for (auto& offer : offers) { + if (offer.user != taskId) { + continue; + } + int64_t toRemove = std::min((int64_t)timeslicesProcessed, offer.timeslices); + offer.timeslices -= toRemove; + timeslicesProcessed -= toRemove; + disposed.timeslices += toRemove; + if (timeslicesProcessed <= 0) { + break; + } + } + return accountDisposed(disposed, stats); + }; + pcx.services().get().offerConsumers.emplace_back(disposeResources); }); } diff --git a/Framework/Core/src/CommonLabels.cxx b/Framework/Core/src/CommonLabels.cxx new file mode 100644 index 0000000000000..f728e194f611b --- /dev/null +++ b/Framework/Core/src/CommonLabels.cxx @@ -0,0 +1,19 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/CommonLabels.h" + +namespace o2::framework +{ + +const DataProcessorLabel suppressDomainInfoLabel = {"suppress-domain-info"}; + +} // namespace o2::framework diff --git a/Framework/Core/src/CommonMessageBackends.cxx b/Framework/Core/src/CommonMessageBackends.cxx index 79bd84307df15..25bf6a138dee4 100644 --- a/Framework/Core/src/CommonMessageBackends.cxx +++ b/Framework/Core/src/CommonMessageBackends.cxx @@ -57,7 +57,18 @@ o2::framework::ServiceSpec CommonMessageBackends::fairMQDeviceProxy() /// some of the channels are added only later on to the party, /// (e.g. by ECS) and Init might not be late enough to /// account for them. - proxy->bind(outputs, inputs, forwards, *device); }, + std::function bindByName = [device](std::string const& channelName) -> fair::mq::Channel& { + auto channel = device->GetChannels().find(channelName); + if (channel == device->GetChannels().end()) { + LOGP(fatal, "Expected channel {} not configured.", channelName); + } + return channel->second.at(0); + }; + + std::function newStateCallback = [device]() -> bool { + return device->NewStatePending(); + }; + proxy->bind(outputs, inputs, forwards, bindByName, newStateCallback); }, }; } diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 5a2876e074d29..06bc7969ebf1e 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -45,6 +45,7 @@ #include "Framework/DefaultsHelpers.h" #include "Framework/Signpost.h" #include "Framework/DriverConfig.h" +#include "Framework/CommonLabels.h" #include "TextDriverClient.h" #include "WSDriverClient.h" @@ -413,11 +414,13 @@ o2::framework::ServiceSpec CommonServices::dataRelayer() .name = "datarelayer", .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { auto& spec = services.get(); + int pipelineLength = DefaultsHelpers::pipelineLength(options); return ServiceHandle{TypeIdHelpers::uniqueId(), new DataRelayer(spec.completionPolicy, spec.inputs, services.get(), - services)}; + services, + pipelineLength)}; }, .configure = noConfiguration(), .kind = ServiceKind::Serial}; @@ -604,6 +607,12 @@ o2::framework::ServiceSpec break; } } + for (const auto& label : services.get().labels) { + if (label == suppressDomainInfoLabel) { + decongestion->suppressDomainInfo = true; + break; + } + } auto& queue = services.get(); decongestion->oldestPossibleTimesliceTask = AsyncQueueHelpers::create(queue, {.name = "oldest-possible-timeslice", .score = 100}); return ServiceHandle{TypeIdHelpers::uniqueId(), decongestion, ServiceKind::Serial}; @@ -1080,6 +1089,38 @@ o2::framework::ServiceSpec CommonServices::dataProcessingStats() .minPublishInterval = 0, .maxRefreshLatency = 10000, .sendInitialValue = true}, + MetricSpec{.name = "timeslice-offer-number-consumed", + .enabled = arrowAndResourceLimitingMetrics, + .metricId = static_cast(ProcessingStatsId::TIMESLICE_OFFER_NUMBER_CONSUMED), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 0, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "timeslices-expired", + .enabled = arrowAndResourceLimitingMetrics, + .metricId = static_cast(ProcessingStatsId::TIMESLICE_NUMBER_EXPIRED), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 0, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "timeslices-started", + .enabled = arrowAndResourceLimitingMetrics, + .metricId = static_cast(ProcessingStatsId::TIMESLICE_NUMBER_STARTED), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 0, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "timeslices-done", + .enabled = arrowAndResourceLimitingMetrics, + .metricId = static_cast(ProcessingStatsId::TIMESLICE_NUMBER_DONE), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 0, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, MetricSpec{.name = "resources-missing", .enabled = enableDebugMetrics, .metricId = static_cast(ProcessingStatsId::RESOURCES_MISSING), @@ -1111,6 +1152,30 @@ o2::framework::ServiceSpec CommonServices::dataProcessingStats() .scope = Scope::DPL, .minPublishInterval = 0, .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-hit", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_HIT), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-miss", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_MISS), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-failure", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_FAILURE), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, .sendInitialValue = true}}; for (auto& metric : metrics) { @@ -1237,17 +1302,6 @@ o2::framework::ServiceSpec CommonServices::dataProcessorContextSpec() .kind = ServiceKind::Serial}; } -o2::framework::ServiceSpec CommonServices::deviceContextSpec() -{ - return ServiceSpec{ - .name = "device-context", - .init = [](ServiceRegistryRef, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle { - return ServiceHandle{TypeIdHelpers::uniqueId(), new DeviceContext()}; - }, - .configure = noConfiguration(), - .kind = ServiceKind::Serial}; -} - o2::framework::ServiceSpec CommonServices::dataAllocatorSpec() { return ServiceSpec{ diff --git a/Framework/Core/src/CompletionPolicy.cxx b/Framework/Core/src/CompletionPolicy.cxx index ec8997e32c5db..a09028b9249f3 100644 --- a/Framework/Core/src/CompletionPolicy.cxx +++ b/Framework/Core/src/CompletionPolicy.cxx @@ -26,11 +26,7 @@ std::vector { return { CompletionPolicyHelpers::consumeWhenAllOrdered("internal-dpl-aod-writer"), -#if __has_include() CompletionPolicyHelpers::consumeWhenAnyZeroCount("internal-dpl-injected-dummy-sink", [](DeviceSpec const& s) { return s.name.find("internal-dpl-injected-dummy-sink") != std::string::npos; }), -#else - CompletionPolicyHelpers::consumeWhenAny("internal-dpl-injected-dummy-sink", [](DeviceSpec const& s) { return s.name.find("internal-dpl-injected-dummy-sink") != std::string::npos; }), -#endif CompletionPolicyHelpers::consumeWhenAll()}; } diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index e682f9a7c7dd6..2b49b8dfa9acd 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -11,6 +11,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "Framework/CompletionPolicy.h" +#include "Framework/DataProcessingHeader.h" #include "Framework/InputSpan.h" #include "Framework/DeviceSpec.h" #include "Framework/CompilerBuiltins.h" @@ -19,9 +20,7 @@ #include "Framework/TimingInfo.h" #include "DecongestionService.h" #include "Framework/Signpost.h" -#if __has_include() #include -#endif #include #include @@ -252,7 +251,6 @@ CompletionPolicy CompletionPolicyHelpers::consumeExistingWhenAny(const char* nam }}; } -#if __has_include() CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* name, CompletionPolicy::Matcher matcher) { auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { @@ -265,7 +263,32 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* na }; return CompletionPolicy{name, matcher, callback, false}; } -#endif + +CompletionPolicy CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher) +{ + auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { + size_t currentTimeslice = -1; + for (auto& input : inputs) { + if (input.header == nullptr) { + continue; + } + o2::framework::DataProcessingHeader const* dph = o2::header::get(input.header); + if (dph && !TimingInfo::timesliceIsTimer(dph->startTime)) { + currentTimeslice = dph->startTime; + break; + } + } + + auto& timesliceIndex = ref.get(); + auto oldestPossibleTimeslice = timesliceIndex.getOldestPossibleInput().timeslice.value; + + if (currentTimeslice >= oldestPossibleTimeslice) { + return CompletionPolicy::CompletionOp::Retry; + } + return CompletionPolicy::CompletionOp::Consume; + }; + return CompletionPolicy{name, matcher, callback, false}; +} CompletionPolicy CompletionPolicyHelpers::consumeWhenAny(const char* name, CompletionPolicy::Matcher matcher) { diff --git a/Framework/Core/src/ComputingQuotaEvaluator.cxx b/Framework/Core/src/ComputingQuotaEvaluator.cxx index 717a59f5f5372..3f5bff2b53fab 100644 --- a/Framework/Core/src/ComputingQuotaEvaluator.cxx +++ b/Framework/Core/src/ComputingQuotaEvaluator.cxx @@ -36,14 +36,14 @@ ComputingQuotaEvaluator::ComputingQuotaEvaluator(ServiceRegistryRef ref) // so this will only work with some device which does not require // any CPU. Notice this will have troubles if a given DPL process // runs for more than a year. - mOffers[0] = { - 0, - 0, - 0, - -1, - -1, - OfferScore::Unneeded, - true}; + mOffers[0] = ComputingQuotaOffer{ + .cpu = 0, + .memory = 0, + .sharedMemory = 0, + .timeslices = 0, + .runtime = -1, + .score = OfferScore::Unneeded, + .valid = true}; mInfos[0] = { uv_now(state.loop), 0, @@ -97,7 +97,7 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& result.size(), totalOffer.cpu, totalOffer.memory, totalOffer.sharedMemory); for (auto& offer : result) { // We pretend each offer id is a pointer, to have a unique id. - O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(offer*8)); + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(offer * 8)); O2_SIGNPOST_START(quota, oid, "offers", "Offer %d has been selected.", offer); } dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCES_SATISFACTORY), DataProcessingStats::Op::Add, 1}); @@ -132,6 +132,7 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& auto& offer = mOffers[i]; auto& info = mInfos[i]; if (enough) { + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "We have enough offers. We can continue for computation."); break; } // Ignore: @@ -139,24 +140,26 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& // - Offers which belong to another task // - Expired offers if (offer.valid == false) { + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d is not valid. Skipping", i); stats.invalidOffers.push_back(i); continue; } if (offer.user != -1 && offer.user != task) { + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d already offered to some other user", i); stats.otherUser.push_back(i); continue; } if (offer.runtime < 0) { stats.unexpiring.push_back(i); } else if (offer.runtime + info.received < now) { - O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d expired since %llu milliseconds and holds %llu MB", - i, now - offer.runtime - info.received, offer.sharedMemory / 1000000); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d expired since %llu milliseconds and holds %llu MB and %llu timeslices", + i, now - offer.runtime - info.received, offer.sharedMemory / 1000000, offer.timeslices); mExpiredOffers.push_back(ComputingQuotaOfferRef{i}); stats.expired.push_back(i); continue; } else { - O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d still valid for %llu milliseconds, providing %llu MB", - i, offer.runtime + info.received - now, offer.sharedMemory / 1000000); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d still valid for %llu milliseconds, providing %llu MB and %llu timeslices", + i, offer.runtime + info.received - now, offer.sharedMemory / 1000000, offer.timeslices); if (minValidity == 0) { minValidity = offer.runtime + info.received - now; } @@ -168,22 +171,29 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& tmp.cpu += offer.cpu; tmp.memory += offer.memory; tmp.sharedMemory += offer.sharedMemory; - offer.score = selector(offer, tmp); + tmp.timeslices += offer.timeslices; + offer.score = selector(offer, accumulated); switch (offer.score) { case OfferScore::Unneeded: + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d considered not needed. Skipping", i); continue; case OfferScore::Unsuitable: + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d considered Unsuitable. Skipping", i); continue; case OfferScore::More: selectOffer(i, now); accumulated = tmp; stats.selectedOffers.push_back(i); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d selected but not enough. %llu MB, %d cores and %llu timeslices are not enough.", + i, tmp.sharedMemory / 1000000, tmp.cpu, tmp.timeslices); continue; case OfferScore::Enough: selectOffer(i, now); accumulated = tmp; stats.selectedOffers.push_back(i); enough = true; + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Selected %zu offers providing %llu MB, %d cores and %llu timeslices are deemed enough.", + stats.selectedOffers.size(), tmp.sharedMemory / 1000000, tmp.cpu, tmp.timeslices); break; }; } @@ -224,7 +234,7 @@ void ComputingQuotaEvaluator::dispose(int taskId) continue; } if (offer.sharedMemory <= 0) { - O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(oi*8)); + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(oi * 8)); O2_SIGNPOST_END(quota, oid, "offers", "Offer %d back to not needed.", oi); offer.valid = false; offer.score = OfferScore::Unneeded; @@ -235,21 +245,47 @@ void ComputingQuotaEvaluator::dispose(int taskId) /// Move offers from the pending list to the actual available offers void ComputingQuotaEvaluator::updateOffers(std::vector& pending, uint64_t now) { + O2_SIGNPOST_ID_GENERATE(oid, quota); + O2_SIGNPOST_START(quota, oid, "updateOffers", "Starting to process %zu received offers", pending.size()); + int lastValid = -1; for (size_t oi = 0; oi < mOffers.size(); oi++) { auto& storeOffer = mOffers[oi]; auto& info = mInfos[oi]; if (pending.empty()) { + O2_SIGNPOST_END(quota, oid, "updateOffers", "No more pending offers to process"); return; } if (storeOffer.valid == true) { + O2_SIGNPOST_EVENT_EMIT(quota, oid, "updateOffers", "Skipping update of offer %zu because it's still valid", oi); + // In general we want to fill an invalid offer. If we do not find any + // we add to the last valid offer we found. + lastValid = oi; continue; } info.received = now; auto& offer = pending.back(); + O2_SIGNPOST_EVENT_EMIT(quota, oid, "updateOffers", "Updating of offer %zu at %llu. Cpu: %d, Shared Memory %lli, Timeslices: %lli", + oi, now, offer.cpu, offer.sharedMemory, offer.timeslices); storeOffer = offer; storeOffer.valid = true; pending.pop_back(); } + if (lastValid == -1) { + O2_SIGNPOST_END_WITH_ERROR(quota, oid, "updateOffers", "ComputingQuotaOffer losts. This should never happen."); + return; + } + auto& lastValidOffer = mOffers[lastValid]; + for (auto& stillPending : pending) { + lastValidOffer.cpu += stillPending.cpu; + lastValidOffer.memory += stillPending.memory; + lastValidOffer.sharedMemory += stillPending.sharedMemory; + lastValidOffer.timeslices += stillPending.timeslices; + lastValidOffer.runtime = std::max(lastValidOffer.runtime, stillPending.runtime); + } + pending.clear(); + auto& updatedOffer = mOffers[lastValid]; + O2_SIGNPOST_END(quota, oid, "updateOffers", "Remaining offers cohalesced to %d. New values: Cpu%d, Shared Memory %lli, Timeslices %lli", + lastValid, updatedOffer.cpu, updatedOffer.sharedMemory, updatedOffer.timeslices); } void ComputingQuotaEvaluator::handleExpired(std::function expirator) @@ -269,24 +305,25 @@ void ComputingQuotaEvaluator::handleExpired(std::function= 0); - mStats.totalExpiredBytes += offer.sharedMemory; + O2_SIGNPOST_END(quota, oid, "handleExpired", "Offer %d expired. Giving back %llu MB, %d cores and %llu timeslices", + ref.index, offer.sharedMemory / 1000000, offer.cpu, offer.timeslices); + mStats.totalExpiredBytes += std::max(offer.sharedMemory, 0); + mStats.totalExpiredTimeslices += std::max(offer.timeslices, 0); mStats.totalExpiredOffers++; expirator(offer, mStats); // driverClient.tell("expired shmem {}", offer.sharedMemory); // driverClient.tell("expired cpu {}", offer.cpu); offer.sharedMemory = -1; + offer.timeslices = -1; offer.valid = false; offer.score = OfferScore::Unneeded; } diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 5564f68d8f8ce..da04a23e81c0c 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/AsyncQueue.h" #include "Framework/DataProcessingDevice.h" +#include #include "Framework/ControlService.h" #include "Framework/ComputingQuotaEvaluator.h" #include "Framework/DataProcessingHeader.h" @@ -17,6 +18,7 @@ #include "Framework/DataProcessor.h" #include "Framework/DataSpecUtils.h" #include "Framework/DeviceState.h" +#include "Framework/DeviceStateEnums.h" #include "Framework/DispatchPolicy.h" #include "Framework/DispatchControl.h" #include "Framework/DanglingContext.h" @@ -57,9 +59,7 @@ #include #include #include -#if __has_include() #include -#endif #include #include #include @@ -98,6 +98,8 @@ O2_DECLARE_DYNAMIC_LOG(async_queue); O2_DECLARE_DYNAMIC_LOG(forwarding); // Special log to track CCDB related requests O2_DECLARE_DYNAMIC_LOG(ccdb); +// Special log to track task scheduling +O2_DECLARE_DYNAMIC_LOG(scheduling); using namespace o2::framework; using ConfigurationInterface = o2::configuration::ConfigurationInterface; @@ -121,63 +123,6 @@ void on_idle_timer(uv_timer_t* handle) state->loopReason |= DeviceState::TIMER_EXPIRED; } -bool hasOnlyTimers(DeviceSpec const& spec) -{ - return std::all_of(spec.inputs.cbegin(), spec.inputs.cend(), [](InputRoute const& route) -> bool { return route.matcher.lifetime == Lifetime::Timer; }); -} - -bool hasOnlyGenerated(DeviceSpec const& spec) -{ - return (spec.inputChannels.size() == 1) && (spec.inputs[0].matcher.lifetime == Lifetime::Timer || spec.inputs[0].matcher.lifetime == Lifetime::Enumeration); -} - -void on_transition_requested_expired(uv_timer_t* handle) -{ - auto* ref = (ServiceRegistryRef*)handle->data; - auto& state = ref->get(); - state.loopReason |= DeviceState::TIMER_EXPIRED; - // Check if this is a source device - O2_SIGNPOST_ID_FROM_POINTER(cid, device, handle); - auto& spec = ref->get(); - if (hasOnlyGenerated(spec)) { - O2_SIGNPOST_EVENT_EMIT_ERROR(calibration, cid, "callback", "DPL exit transition grace period for source expired. Exiting."); - } else { - O2_SIGNPOST_EVENT_EMIT_ERROR(calibration, cid, "callback", "DPL exit transition grace period for %{public}s expired. Exiting.", - state.allowedProcessing == DeviceState::CalibrationOnly ? "calibration" : "data & calibration"); - } - state.transitionHandling = TransitionHandlingState::Expired; -} - -auto switchState(ServiceRegistryRef& ref, StreamingState newState) -> void -{ - auto& state = ref.get(); - auto& context = ref.get(); - O2_SIGNPOST_ID_FROM_POINTER(dpid, device, &context); - O2_SIGNPOST_END(device, dpid, "state", "End of processing state %d", (int)state.streaming); - O2_SIGNPOST_START(device, dpid, "state", "Starting processing state %d", (int)newState); - state.streaming = newState; - ref.get().notifyStreamingState(state.streaming); -}; - -void on_data_processing_expired(uv_timer_t* handle) -{ - auto* ref = (ServiceRegistryRef*)handle->data; - auto& state = ref->get(); - auto& spec = ref->get(); - state.loopReason |= DeviceState::TIMER_EXPIRED; - - // Check if this is a source device - O2_SIGNPOST_ID_FROM_POINTER(cid, device, handle); - - if (hasOnlyGenerated(spec)) { - O2_SIGNPOST_EVENT_EMIT_INFO(calibration, cid, "callback", "Grace period for data processing expired. Switching to EndOfStreaming."); - switchState(*ref, StreamingState::EndOfStreaming); - } else { - O2_SIGNPOST_EVENT_EMIT_INFO(calibration, cid, "callback", "Grace period for data processing expired. Only calibrations from this point onwards."); - state.allowedProcessing = DeviceState::CalibrationOnly; - } -} - void on_communication_requested(uv_async_t* s) { auto* state = (DeviceState*)s->data; @@ -196,11 +141,10 @@ struct locked_execution { ~locked_execution() { ref.unlock(); } }; -DataProcessingDevice::DataProcessingDevice(RunningDeviceRef running, ServiceRegistry& registry, ProcessingPolicies& policies) +DataProcessingDevice::DataProcessingDevice(RunningDeviceRef running, ServiceRegistry& registry) : mRunningDevice{running}, mConfigRegistry{nullptr}, - mServiceRegistry{registry}, - mProcessingPolicies{policies} + mServiceRegistry{registry} { GetConfig()->Subscribe("dpl", [®istry = mServiceRegistry](const std::string& key, std::string value) { if (key == "cleanup") { @@ -247,6 +191,7 @@ DataProcessingDevice::DataProcessingDevice(RunningDeviceRef running, ServiceRegi mHandles.resize(1); ServiceRegistryRef ref{mServiceRegistry}; + mAwakeHandle = (uv_async_t*)malloc(sizeof(uv_async_t)); auto& state = ref.get(); assert(state.loop); @@ -300,16 +245,22 @@ void run_completion(uv_work_t* handle, int status) static std::function reportConsumedOffer = [ref](ComputingQuotaOffer const& accumulatedConsumed, ComputingQuotaStats& stats) { auto& dpStats = ref.get(); stats.totalConsumedBytes += accumulatedConsumed.sharedMemory; + // For now we give back the offer if we did not use it completely. + // In principle we should try to run until the offer is fully consumed. + stats.totalConsumedTimeslices += std::min(accumulatedConsumed.timeslices, 1); dpStats.updateStats({static_cast(ProcessingStatsId::SHM_OFFER_BYTES_CONSUMED), DataProcessingStats::Op::Set, stats.totalConsumedBytes}); + dpStats.updateStats({static_cast(ProcessingStatsId::TIMESLICE_OFFER_NUMBER_CONSUMED), DataProcessingStats::Op::Set, stats.totalConsumedTimeslices}); dpStats.processCommandQueue(); assert(stats.totalConsumedBytes == dpStats.metrics[(short)ProcessingStatsId::SHM_OFFER_BYTES_CONSUMED]); + assert(stats.totalConsumedTimeslices == dpStats.metrics[(short)ProcessingStatsId::TIMESLICE_OFFER_NUMBER_CONSUMED]); }; static std::function reportExpiredOffer = [ref](ComputingQuotaOffer const& offer, ComputingQuotaStats const& stats) { auto& dpStats = ref.get(); dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCE_OFFER_EXPIRED), DataProcessingStats::Op::Set, stats.totalExpiredOffers}); dpStats.updateStats({static_cast(ProcessingStatsId::ARROW_BYTES_EXPIRED), DataProcessingStats::Op::Set, stats.totalExpiredBytes}); + dpStats.updateStats({static_cast(ProcessingStatsId::TIMESLICE_NUMBER_EXPIRED), DataProcessingStats::Op::Set, stats.totalExpiredTimeslices}); dpStats.processCommandQueue(); }; @@ -597,76 +548,6 @@ void on_signal_callback(uv_signal_t* handle, int signum) O2_SIGNPOST_END(device, sid, "signal_state", "Done processing signals."); } -static auto toBeForwardedHeader = [](void* header) -> bool { - // If is now possible that the record is not complete when - // we forward it, because of a custom completion policy. - // this means that we need to skip the empty entries in the - // record for being forwarded. - if (header == nullptr) { - return false; - } - auto sih = o2::header::get(header); - if (sih) { - return false; - } - - auto dih = o2::header::get(header); - if (dih) { - return false; - } - - auto dh = o2::header::get(header); - if (!dh) { - return false; - } - auto dph = o2::header::get(header); - if (!dph) { - return false; - } - return true; -}; - -static auto toBeforwardedMessageSet = [](std::vector& cachedForwardingChoices, - FairMQDeviceProxy& proxy, - std::unique_ptr& header, - std::unique_ptr& payload, - size_t total, - bool consume) { - if (header.get() == nullptr) { - // Missing an header is not an error anymore. - // it simply means that we did not receive the - // given input, but we were asked to - // consume existing, so we skip it. - return false; - } - if (payload.get() == nullptr && consume == true) { - // If the payload is not there, it means we already - // processed it with ConsumeExisiting. Therefore we - // need to do something only if this is the last consume. - header.reset(nullptr); - return false; - } - - auto fdph = o2::header::get(header->GetData()); - if (fdph == nullptr) { - LOG(error) << "Data is missing DataProcessingHeader"; - return false; - } - auto fdh = o2::header::get(header->GetData()); - if (fdh == nullptr) { - LOG(error) << "Data is missing DataHeader"; - return false; - } - - // We need to find the forward route only for the first - // part of a split payload. All the others will use the same. - // but always check if we have a sequence of multiple payloads - if (fdh->splitPayloadIndex == 0 || fdh->splitPayloadParts <= 1 || total > 1) { - proxy.getMatchingForwardChannelIndexes(cachedForwardingChoices, *fdh, fdph->startTime); - } - return cachedForwardingChoices.empty() == false; -}; - struct DecongestionContext { ServiceRegistryRef ref; TimesliceIndex::OldestOutputInfo oldestTimeslice; @@ -707,68 +588,12 @@ auto decongestionCallbackLate = [](AsyncTask& task, size_t aid) -> void { static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector& currentSetOfInputs, TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume = true) { auto& proxy = registry.get(); - // we collect all messages per forward in a map and send them together - std::vector forwardedParts; - forwardedParts.resize(proxy.getNumForwards()); - std::vector cachedForwardingChoices{}; + O2_SIGNPOST_ID_GENERATE(sid, forwarding); O2_SIGNPOST_START(forwarding, sid, "forwardInputs", "Starting forwarding for slot %zu with oldestTimeslice %zu %{public}s%{public}s%{public}s", slot.index, oldestTimeslice.timeslice.value, copy ? "with copy" : "", copy && consume ? " and " : "", consume ? "with consume" : ""); + auto forwardedParts = DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copy, consume); - for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { - auto& messageSet = currentSetOfInputs[ii]; - // In case the messageSet is empty, there is nothing to be done. - if (messageSet.size() == 0) { - continue; - } - if (!toBeForwardedHeader(messageSet.header(0)->GetData())) { - continue; - } - cachedForwardingChoices.clear(); - - for (size_t pi = 0; pi < currentSetOfInputs[ii].size(); ++pi) { - auto& messageSet = currentSetOfInputs[ii]; - auto& header = messageSet.header(pi); - auto& payload = messageSet.payload(pi); - auto total = messageSet.getNumberOfPayloads(pi); - - if (!toBeforwardedMessageSet(cachedForwardingChoices, proxy, header, payload, total, consume)) { - continue; - } - - // In case of more than one forward route, we need to copy the message. - // This will eventually use the same mamory if running with the same backend. - if (cachedForwardingChoices.size() > 1) { - copy = true; - } - auto* dh = o2::header::get(header->GetData()); - auto* dph = o2::header::get(header->GetData()); - - if (copy) { - for (auto& cachedForwardingChoice : cachedForwardingChoices) { - auto&& newHeader = header->GetTransport()->CreateMessage(); - O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding a copy of %{public}s to route %d.", - fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), cachedForwardingChoice.value); - newHeader->Copy(*header); - forwardedParts[cachedForwardingChoice.value].AddPart(std::move(newHeader)); - - for (size_t payloadIndex = 0; payloadIndex < messageSet.getNumberOfPayloads(pi); ++payloadIndex) { - auto&& newPayload = header->GetTransport()->CreateMessage(); - newPayload->Copy(*messageSet.payload(pi, payloadIndex)); - forwardedParts[cachedForwardingChoice.value].AddPart(std::move(newPayload)); - } - } - } else { - O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding %{public}s to route %d.", - fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), cachedForwardingChoices.back().value); - forwardedParts[cachedForwardingChoices.back().value].AddPart(std::move(messageSet.header(pi))); - for (size_t payloadIndex = 0; payloadIndex < messageSet.getNumberOfPayloads(pi); ++payloadIndex) { - forwardedParts[cachedForwardingChoices.back().value].AddPart(std::move(messageSet.payload(pi, payloadIndex))); - } - } - } - } - O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding %zu messages", forwardedParts.size()); for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) { if (forwardedParts[fi].Size() == 0) { continue; @@ -792,6 +617,23 @@ static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Forwarding done"); }; +static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector& currentSetOfInputs, + TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume = true) { + auto& proxy = registry.get(); + + O2_SIGNPOST_ID_GENERATE(sid, forwarding); + O2_SIGNPOST_START(forwarding, sid, "forwardInputs", "Cleaning up slot %zu with oldestTimeslice %zu %{public}s%{public}s%{public}s", + slot.index, oldestTimeslice.timeslice.value, copy ? "with copy" : "", copy && consume ? " and " : "", consume ? "with consume" : ""); + // Always copy them, because we do not want to actually send them. + // We merely need the side effect of the consume, if applicable. + for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { + auto span = std::span(currentSetOfInputs[ii].messages); + DataProcessingHelpers::cleanForwardedMessages(span, consume); + } + + O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Cleaning done"); +}; + extern volatile int region_read_global_dummy_variable; volatile int region_read_global_dummy_variable; @@ -1189,18 +1031,18 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont errorCallback(errorContext); }; } else { - context.errorHandling = [&errorPolicy = mProcessingPolicies.error, - &serviceRegistry = mServiceRegistry](RuntimeErrorRef e, InputRecord& record) { + context.errorHandling = [&serviceRegistry = mServiceRegistry](RuntimeErrorRef e, InputRecord& record) { auto& err = error_from_ref(e); /// FIXME: we should pass the salt in, so that the message /// can access information which were stored in the stream. ServiceRegistryRef ref{serviceRegistry, ServiceRegistry::globalDeviceSalt()}; auto& context = ref.get(); + auto& deviceContext = ref.get(); O2_SIGNPOST_ID_FROM_POINTER(cid, device, &context); BacktraceHelpers::demangled_backtrace_symbols(err.backtrace, err.maxBacktrace, STDERR_FILENO); auto& stats = ref.get(); stats.updateStats({(int)ProcessingStatsId::EXCEPTION_COUNT, DataProcessingStats::Op::Add, 1}); - switch (errorPolicy) { + switch (deviceContext.processingPolicies.error) { case TerminationPolicy::QUIT: O2_SIGNPOST_EVENT_EMIT_ERROR(device, cid, "Run", "Exception while running: %{public}s. Rethrowing.", err.what); throw e; @@ -1211,44 +1053,75 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont }; } - auto decideEarlyForward = [&context, &spec, this]() -> bool { + auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> ForwardPolicy { + ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + // FIXME: try again with the new policy by default. + // + // Make the new policy optional until we handle some of the corner cases + // with custom policies which expect the early forward to happen only when + // all the data is available, like in the TPC case. + // ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_NEW_EARLY_FORWARD") ? ForwardPolicy::AtInjection : ForwardPolicy::AtCompletionPolicySatisified; + for (auto& forward : spec.forwards) { + if (DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{"TPC", "DIGITSMCTR"}) || + DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}) || + DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "DIGITS"}) || + DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "CLUSTERNATIVE"})) { + defaultEarlyForwardPolicy = ForwardPolicy::AtCompletionPolicySatisified; + break; + } + } + // Output proxies should wait for the completion policy before forwarding. + // Because they actually do not do anything, that's equivalent to + // forwarding after the processing. + for (auto& label : spec.labels) { + if (label.value == "output-proxy") { + defaultEarlyForwardPolicy = ForwardPolicy::AfterProcessing; + break; + } + } + /// We must make sure there is no optional /// if we want to optimize the forwarding - bool canForwardEarly = (spec.forwards.empty() == false) && mProcessingPolicies.earlyForward != EarlyForwardPolicy::NEVER; + ForwardPolicy forwardPolicy = defaultEarlyForwardPolicy; + if (spec.forwards.empty() == false) { + switch (deviceContext.processingPolicies.earlyForward) { + case o2::framework::EarlyForwardPolicy::NEVER: + forwardPolicy = ForwardPolicy::AfterProcessing; + break; + case o2::framework::EarlyForwardPolicy::ALWAYS: + forwardPolicy = defaultEarlyForwardPolicy; + break; + case o2::framework::EarlyForwardPolicy::NORAW: + forwardPolicy = defaultEarlyForwardPolicy; + break; + } + } bool onlyConditions = true; bool overriddenEarlyForward = false; for (auto& forwarded : spec.forwards) { if (forwarded.matcher.lifetime != Lifetime::Condition) { onlyConditions = false; } -#if !__has_include() - if (strncmp(DataSpecUtils::asConcreteOrigin(forwarded.matcher).str, "AOD", 3) == 0) { - context.canForwardEarly = false; - overriddenEarlyForward = true; - LOG(detail) << "Cannot forward early because of AOD input: " << DataSpecUtils::describe(forwarded.matcher); - break; - } -#endif - if (DataSpecUtils::partialMatch(forwarded.matcher, o2::header::DataDescription{"RAWDATA"}) && mProcessingPolicies.earlyForward == EarlyForwardPolicy::NORAW) { - context.canForwardEarly = false; + if (DataSpecUtils::partialMatch(forwarded.matcher, o2::header::DataDescription{"RAWDATA"}) && deviceContext.processingPolicies.earlyForward == EarlyForwardPolicy::NORAW) { + forwardPolicy = ForwardPolicy::AfterProcessing; overriddenEarlyForward = true; LOG(detail) << "Cannot forward early because of RAWDATA input: " << DataSpecUtils::describe(forwarded.matcher); break; } if (forwarded.matcher.lifetime == Lifetime::Optional) { - context.canForwardEarly = false; + forwardPolicy = ForwardPolicy::AfterProcessing; overriddenEarlyForward = true; LOG(detail) << "Cannot forward early because of Optional input: " << DataSpecUtils::describe(forwarded.matcher); break; } } if (!overriddenEarlyForward && onlyConditions) { - context.canForwardEarly = true; + forwardPolicy = defaultEarlyForwardPolicy; LOG(detail) << "Enabling early forwarding because only conditions to be forwarded"; } - return canForwardEarly; + return forwardPolicy; }; - context.canForwardEarly = decideEarlyForward(); + context.forwardPolicy = decideEarlyForward(); } void DataProcessingDevice::PreRun() @@ -1259,7 +1132,7 @@ void DataProcessingDevice::PreRun() O2_SIGNPOST_ID_FROM_POINTER(cid, device, state.loop); O2_SIGNPOST_START(device, cid, "PreRun", "Entering PreRun callback."); state.quitRequested = false; - switchState(ref, StreamingState::Streaming); + DataProcessingHelpers::switchState(ref, StreamingState::Streaming); state.allowedProcessing = DeviceState::Any; for (auto& info : state.inputChannelInfos) { if (info.state != InputChannelState::Pull) { @@ -1382,51 +1255,7 @@ void DataProcessingDevice::Run() shouldNotWait = true; state.loopReason |= DeviceState::LoopReason::NEW_STATE_PENDING; } - if (state.transitionHandling == TransitionHandlingState::NoTransition && NewStatePending()) { - state.transitionHandling = TransitionHandlingState::Requested; - auto& deviceContext = ref.get(); - // Check if we only have timers - auto& spec = ref.get(); - if (hasOnlyTimers(spec)) { - switchState(ref, StreamingState::EndOfStreaming); - } - - // We do not do anything in particular if the data processing timeout would go past the exitTransitionTimeout - if (deviceContext.dataProcessingTimeout > 0 && deviceContext.dataProcessingTimeout < deviceContext.exitTransitionTimeout) { - uv_update_time(state.loop); - O2_SIGNPOST_EVENT_EMIT(calibration, lid, "timer_setup", "Starting %d s timer for dataProcessingTimeout.", deviceContext.dataProcessingTimeout); - uv_timer_start(deviceContext.dataProcessingGracePeriodTimer, on_data_processing_expired, deviceContext.dataProcessingTimeout * 1000, 0); - } - if (deviceContext.exitTransitionTimeout != 0 && state.streaming != StreamingState::Idle) { - state.transitionHandling = TransitionHandlingState::Requested; - ref.get().call(ServiceRegistryRef{ref}); - uv_update_time(state.loop); - O2_SIGNPOST_EVENT_EMIT(calibration, lid, "timer_setup", "Starting %d s timer for exitTransitionTimeout.", - deviceContext.exitTransitionTimeout); - uv_timer_start(deviceContext.gracePeriodTimer, on_transition_requested_expired, deviceContext.exitTransitionTimeout * 1000, 0); - bool onlyGenerated = hasOnlyGenerated(spec); - int timeout = onlyGenerated ? deviceContext.dataProcessingTimeout : deviceContext.exitTransitionTimeout; - if (mProcessingPolicies.termination == TerminationPolicy::QUIT && DefaultsHelpers::onlineDeploymentMode() == false) { - O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state requested. Waiting for %d seconds before quitting.", timeout); - } else { - O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", - "New state requested. Waiting for %d seconds before %{public}s", - timeout, - onlyGenerated ? "dropping remaining input and switching to READY state." : "switching to READY state."); - } - } else { - state.transitionHandling = TransitionHandlingState::Expired; - if (deviceContext.exitTransitionTimeout == 0 && mProcessingPolicies.termination == TerminationPolicy::QUIT) { - O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state requested. No timeout set, quitting immediately as per --completion-policy"); - } else if (deviceContext.exitTransitionTimeout == 0 && mProcessingPolicies.termination != TerminationPolicy::QUIT) { - O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state requested. No timeout set, switching to READY state immediately"); - } else if (mProcessingPolicies.termination == TerminationPolicy::QUIT) { - O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state pending and we are already idle, quitting immediately as per --completion-policy"); - } else { - O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state pending and we are already idle, switching to READY immediately."); - } - } - } + state.transitionHandling = DataProcessingHelpers::updateStateTransition(ref, ref.get().processingPolicies); // If we are Idle, we can then consider the transition to be expired. if (state.transitionHandling == TransitionHandlingState::Requested && state.streaming == StreamingState::Idle) { O2_SIGNPOST_EVENT_EMIT(device, lid, "run_loop", "State transition requested and we are now in Idle. We can consider it to be completed."); @@ -1532,6 +1361,7 @@ void DataProcessingDevice::Run() auto& dpStats = ref.get(); dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCE_OFFER_EXPIRED), DataProcessingStats::Op::Set, stats.totalExpiredOffers}); dpStats.updateStats({static_cast(ProcessingStatsId::ARROW_BYTES_EXPIRED), DataProcessingStats::Op::Set, stats.totalExpiredBytes}); + dpStats.updateStats({static_cast(ProcessingStatsId::TIMESLICE_NUMBER_EXPIRED), DataProcessingStats::Op::Set, stats.totalExpiredTimeslices}); dpStats.processCommandQueue(); }; auto ref = ServiceRegistryRef{mServiceRegistry}; @@ -1542,10 +1372,22 @@ void DataProcessingDevice::Run() auto& spec = ref.get(); bool enough = ref.get().selectOffer(streamRef.index, spec.resourcePolicy.request, uv_now(state.loop)); + struct SchedulingStats { + std::atomic lastScheduled = 0; + std::atomic numberOfUnscheduledSinceLastScheduled = 0; + std::atomic numberOfUnscheduled = 0; + std::atomic numberOfScheduled = 0; + }; + static SchedulingStats schedulingStats; + O2_SIGNPOST_ID_GENERATE(sid, scheduling); if (enough) { stream.id = streamRef; stream.running = true; stream.registry = &mServiceRegistry; + schedulingStats.lastScheduled = uv_now(state.loop); + schedulingStats.numberOfScheduled++; + schedulingStats.numberOfUnscheduledSinceLastScheduled = 0; + O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", "Enough resources to schedule computation on stream %d", streamRef.index); if (dplEnableMultithreding) [[unlikely]] { stream.task = &handle; uv_queue_work(state.loop, stream.task, run_callback, run_completion); @@ -1554,13 +1396,27 @@ void DataProcessingDevice::Run() run_completion(&handle, 0); } } else { + if (schedulingStats.numberOfUnscheduledSinceLastScheduled > 100 || + (uv_now(state.loop) - schedulingStats.lastScheduled) > 30000) { + O2_SIGNPOST_EVENT_EMIT_WARN(scheduling, sid, "Run", + "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu. Data is not lost and it will be scheduled again.", + schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), + schedulingStats.lastScheduled.load()); + } else { + O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", + "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu. Data is not lost and it will be scheduled again.", + schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), + schedulingStats.lastScheduled.load()); + } + schedulingStats.numberOfUnscheduled++; + schedulingStats.numberOfUnscheduledSinceLastScheduled++; auto ref = ServiceRegistryRef{mServiceRegistry}; ref.get().handleExpired(reportExpiredOffer); } } } - O2_SIGNPOST_END(device, lid, "run_loop", "Run loop completed. Transition handling state %d.", state.transitionHandling); + O2_SIGNPOST_END(device, lid, "run_loop", "Run loop completed. Transition handling state %d.", (int)state.transitionHandling); auto& spec = ref.get(); /// Cleanup messages which are still pending on exit. for (size_t ci = 0; ci < spec.inputChannels.size(); ++ci) { @@ -1627,7 +1483,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) auto& infos = state.inputChannelInfos; if (context.balancingInputs) { - static int pipelineLength = DefaultsHelpers::pipelineLength(); + static int pipelineLength = DefaultsHelpers::pipelineLength(*ref.get().device()->fConfig); static uint64_t ahead = getenv("DPL_MAX_CHANNEL_AHEAD") ? std::atoll(getenv("DPL_MAX_CHANNEL_AHEAD")) : std::max(8, std::min(pipelineLength - 48, pipelineLength / 2)); auto newEnd = std::remove_if(pollOrder.begin(), pollOrder.end(), [&infos, limitNew = currentOldest.value + ahead](int a) -> bool { return infos[a].oldestForChannel.value > limitNew; @@ -1653,9 +1509,8 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) for (auto sci : pollOrder) { auto& info = state.inputChannelInfos[sci]; - auto& channelSpec = spec.inputChannels[sci]; O2_SIGNPOST_ID_FROM_POINTER(cid, device, &info); - O2_SIGNPOST_START(device, cid, "channels", "Processing channel %s", channelSpec.name.c_str()); + O2_SIGNPOST_START(device, cid, "channels", "Processing channel %s", info.channel->GetName().c_str()); if (info.state != InputChannelState::Completed && info.state != InputChannelState::Pull) { context.allDone = false; @@ -1667,18 +1522,18 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) DataProcessingDevice::handleData(ref, info); } O2_SIGNPOST_END(device, cid, "channels", "Flushing channel %s which is in state %d and has %zu parts still pending.", - channelSpec.name.c_str(), (int)info.state, info.parts.Size()); + info.channel->GetName().c_str(), (int)info.state, info.parts.Size()); continue; } if (info.channel == nullptr) { O2_SIGNPOST_END(device, cid, "channels", "Channel %s which is in state %d is nullptr and has %zu parts still pending.", - channelSpec.name.c_str(), (int)info.state, info.parts.Size()); + info.channel->GetName().c_str(), (int)info.state, info.parts.Size()); continue; } // Only poll DPL channels for now. if (info.channelType != ChannelAccountingType::DPL) { O2_SIGNPOST_END(device, cid, "channels", "Channel %s which is in state %d is not a DPL channel and has %zu parts still pending.", - channelSpec.name.c_str(), (int)info.state, info.parts.Size()); + info.channel->GetName().c_str(), (int)info.state, info.parts.Size()); continue; } auto& socket = info.channel->GetSocket(); @@ -1690,7 +1545,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) socket.Events(&info.hasPendingEvents); // If we do not read, we can continue. if ((info.hasPendingEvents & 1) == 0 && (info.parts.Size() == 0)) { - O2_SIGNPOST_END(device, cid, "channels", "No pending events and no remaining parts to process for channel %{public}s", channelSpec.name.c_str()); + O2_SIGNPOST_END(device, cid, "channels", "No pending events and no remaining parts to process for channel %{public}s", info.channel->GetName().c_str()); continue; } } @@ -1708,12 +1563,12 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) bool newMessages = false; while (true) { O2_SIGNPOST_EVENT_EMIT(device, cid, "channels", "Receiving loop called for channel %{public}s (%d) with oldest possible timeslice %zu", - channelSpec.name.c_str(), info.id.value, info.oldestForChannel.value); + info.channel->GetName().c_str(), info.id.value, info.oldestForChannel.value); if (info.parts.Size() < 64) { fair::mq::Parts parts; info.channel->Receive(parts, 0); if (parts.Size()) { - O2_SIGNPOST_EVENT_EMIT(device, cid, "channels", "Received %zu parts from channel %{public}s (%d).", parts.Size(), channelSpec.name.c_str(), info.id.value); + O2_SIGNPOST_EVENT_EMIT(device, cid, "channels", "Received %zu parts from channel %{public}s (%d).", parts.Size(), info.channel->GetName().c_str(), info.id.value); } for (auto&& part : parts) { info.parts.fParts.emplace_back(std::move(part)); @@ -1742,7 +1597,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) } } O2_SIGNPOST_END(device, cid, "channels", "Done processing channel %{public}s (%d).", - channelSpec.name.c_str(), info.id.value); + info.channel->GetName().c_str(), info.id.value); } } @@ -1785,7 +1640,7 @@ void DataProcessingDevice::doRun(ServiceRegistryRef ref) // dependent on the callback, not something which is controlled by the // framework itself. if (context.allDone == true && state.streaming == StreamingState::Streaming) { - switchState(ref, StreamingState::EndOfStreaming); + DataProcessingHelpers::switchState(ref, StreamingState::EndOfStreaming); state.lastActiveDataProcessor = &context; } @@ -1798,7 +1653,7 @@ void DataProcessingDevice::doRun(ServiceRegistryRef ref) /// timers as they do not need to be further processed. auto& relayer = ref.get(); - bool shouldProcess = hasOnlyGenerated(spec) == false; + bool shouldProcess = DataProcessingHelpers::hasOnlyGenerated(spec) == false; while (DataProcessingDevice::tryDispatchComputation(ref, context.completed) && shouldProcess) { relayer.processDanglingInputs(context.expirationHandlers, *context.registry, false); @@ -1831,7 +1686,7 @@ void DataProcessingDevice::doRun(ServiceRegistryRef ref) } // This is needed because the transport is deleted before the device. relayer.clear(); - switchState(ref, StreamingState::Idle); + DataProcessingHelpers::switchState(ref, StreamingState::Idle); // In case we should process, note the data processor responsible for it if (shouldProcess) { state.lastActiveDataProcessor = &context; @@ -1880,6 +1735,51 @@ struct WaitBackpressurePolicy { } }; +auto forwardOnInsertion(ServiceRegistryRef& ref, std::span& messages) -> void +{ + O2_SIGNPOST_ID_GENERATE(sid, forwarding); + + auto& spec = ref.get(); + auto& context = ref.get(); + if (context.forwardPolicy == ForwardPolicy::AfterProcessing || spec.forwards.empty()) { + O2_SIGNPOST_EVENT_EMIT(device, sid, "device", "Early forwardinding not enabled / needed."); + return; + } + + O2_SIGNPOST_EVENT_EMIT(device, sid, "device", "Early forwardinding before injecting data into relayer."); + auto& timesliceIndex = ref.get(); + auto oldestTimeslice = timesliceIndex.getOldestPossibleOutput(); + + auto& proxy = ref.get(); + + O2_SIGNPOST_START(forwarding, sid, "forwardInputs", + "Starting forwarding for incoming messages with oldestTimeslice %zu with copy", + oldestTimeslice.timeslice.value); + std::vector forwardedParts(proxy.getNumForwardChannels()); + DataProcessingHelpers::routeForwardedMessages(proxy, messages, forwardedParts, true, false); + + for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) { + if (forwardedParts[fi].Size() == 0) { + continue; + } + ForwardChannelInfo info = proxy.getForwardChannelInfo(ChannelIndex{fi}); + auto& parts = forwardedParts[fi]; + if (info.policy == nullptr) { + O2_SIGNPOST_EVENT_EMIT_ERROR(forwarding, sid, "forwardInputs", "Forwarding to %{public}s %d has no policy.", info.name.c_str(), fi); + continue; + } + O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding to %{public}s %d", info.name.c_str(), fi); + info.policy->forward(parts, ChannelIndex{fi}, ref); + } + auto& asyncQueue = ref.get(); + auto& decongestion = ref.get(); + O2_SIGNPOST_ID_GENERATE(aid, async_queue); + O2_SIGNPOST_EVENT_EMIT(async_queue, aid, "forwardInputs", "Queuing forwarding oldestPossible %zu", oldestTimeslice.timeslice.value); + AsyncQueueHelpers::post(asyncQueue, AsyncTask{.timeslice = oldestTimeslice.timeslice, .id = decongestion.oldestPossibleTimesliceTask, .debounce = -1, .callback = decongestionCallbackLate} + .user({.ref = ref, .oldestTimeslice = oldestTimeslice})); + O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Forwarding done"); +}; + /// This is the inner loop of our framework. The actual implementation /// is divided in two parts. In the first one we define a set of lambdas /// which describe what is actually going to happen, hiding all the state @@ -1922,11 +1822,15 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& for (size_t pi = 0; pi < parts.Size(); pi += 2) { auto* headerData = parts.At(pi)->GetData(); auto sih = o2::header::get(headerData); + auto dh = o2::header::get(headerData); if (sih) { O2_SIGNPOST_EVENT_EMIT(device, cid, "handle_data", "Got SourceInfoHeader with state %d", (int)sih->state); info.state = sih->state; insertInputInfo(pi, 2, InputType::SourceInfo, info.id); state.lastActiveDataProcessor = &context; + if (dh) { + LOGP(error, "Found data attached to a SourceInfoHeader"); + } continue; } auto dih = o2::header::get(headerData); @@ -1934,9 +1838,11 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& O2_SIGNPOST_EVENT_EMIT(device, cid, "handle_data", "Got DomainInfoHeader with oldestPossibleTimeslice %d", (int)dih->oldestPossibleTimeslice); insertInputInfo(pi, 2, InputType::DomainInfo, info.id); state.lastActiveDataProcessor = &context; + if (dh) { + LOGP(error, "Found data attached to a DomainInfoHeader"); + } continue; } - auto dh = o2::header::get(headerData); if (!dh) { insertInputInfo(pi, 0, InputType::Invalid, info.id); O2_SIGNPOST_EVENT_EMIT_ERROR(device, cid, "handle_data", "Header is not a DataHeader?"); @@ -1993,7 +1899,7 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& stats.updateStats({(int)ProcessingStatsId::ERROR_COUNT, DataProcessingStats::Op::Add, 1}); }; - auto handleValidMessages = [&info, ref, &reportError](std::vector const& inputInfos) { + auto handleValidMessages = [&info, ref, &reportError, &context](std::vector const& inputInfos) { auto& relayer = ref.get(); auto& state = ref.get(); static WaitBackpressurePolicy policy; @@ -2048,11 +1954,13 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& VariableContextHelpers::getTimeslice(variables); forwardInputs(ref, slot, dropped, oldestOutputInfo, false, true); }; + auto relayed = relayer.relay(parts.At(headerIndex)->GetData(), &parts.At(headerIndex), input, nMessages, nPayloadsPerHeader, + context.forwardPolicy == ForwardPolicy::AtInjection ? forwardOnInsertion : nullptr, onDrop); switch (relayed.type) { case DataRelayer::RelayChoice::Type::Backpressured: @@ -2244,14 +2152,10 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { return currentSetOfInputs[i].getNumberOfPairs(); }; -#if __has_include() auto refCountGetter = [¤tSetOfInputs](size_t idx) -> int { auto& header = static_cast(*currentSetOfInputs[idx].header(0)); return header.GetRefCount(); }; -#else - std::function refCountGetter = nullptr; -#endif return InputSpan{getter, nofPartsGetter, refCountGetter, currentSetOfInputs.size()}; }; @@ -2355,12 +2259,14 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return false; } - auto postUpdateStats = [ref](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart, uint64_t tStartMilli) { + int pipelineLength = DefaultsHelpers::pipelineLength(*ref.get().device()->fConfig); + + auto postUpdateStats = [ref, pipelineLength](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart, uint64_t tStartMilli) { auto& stats = ref.get(); auto& states = ref.get(); std::atomic_thread_fence(std::memory_order_release); char relayerSlotState[1024]; - int written = snprintf(relayerSlotState, 1024, "%d ", DefaultsHelpers::pipelineLength()); + int written = snprintf(relayerSlotState, 1024, "%d ", pipelineLength); char* buffer = relayerSlotState + written; for (size_t ai = 0; ai != record.size(); ai++) { buffer[ai] = record.isValid(ai) ? '3' : '0'; @@ -2387,11 +2293,11 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v count++; }; - auto preUpdateStats = [ref](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t) { + auto preUpdateStats = [ref, pipelineLength](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t) { auto& states = ref.get(); std::atomic_thread_fence(std::memory_order_release); char relayerSlotState[1024]; - snprintf(relayerSlotState, 1024, "%d ", DefaultsHelpers::pipelineLength()); + snprintf(relayerSlotState, 1024, "%d ", pipelineLength); char* buffer = strchr(relayerSlotState, ' ') + 1; for (size_t ai = 0; ai != record.size(); ai++) { buffer[ai] = record.isValid(ai) ? '2' : '0'; @@ -2470,11 +2376,23 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v bool hasForwards = spec.forwards.empty() == false; bool consumeSomething = action.op == CompletionPolicy::CompletionOp::Consume || action.op == CompletionPolicy::CompletionOp::ConsumeExisting; - if (context.canForwardEarly && hasForwards && consumeSomething) { - O2_SIGNPOST_EVENT_EMIT(device, aid, "device", "Early forwainding: %{public}s.", fmt::format("{}", action.op).c_str()); + if (context.forwardPolicy == ForwardPolicy::AtCompletionPolicySatisified && hasForwards && consumeSomething) { + O2_SIGNPOST_EVENT_EMIT(device, aid, "device", "Early forwarding: %{public}s.", fmt::format("{}", action.op).c_str()); auto& timesliceIndex = ref.get(); forwardInputs(ref, action.slot, currentSetOfInputs, timesliceIndex.getOldestPossibleOutput(), true, action.op == CompletionPolicy::CompletionOp::Consume); + } else if (context.forwardPolicy == ForwardPolicy::AtInjection && hasForwards && consumeSomething) { + // We used to do fowarding here, however we now do it much earlier. + // We still need to clean the inputs which were already consumed + // via ConsumeExisting and which still have an header to hold the slot. + // FIXME: do we? This should really happen when we do the forwarding on + // insertion, because otherwise we lose the relevant information on how to + // navigate the set of headers. We could actually rely on the messageset index, + // is that the right thing to do though? + O2_SIGNPOST_EVENT_EMIT(device, aid, "device", "cleaning early forwarding: %{public}s.", fmt::format("{}", action.op).c_str()); + auto& timesliceIndex = ref.get(); + cleanEarlyForward(ref, action.slot, currentSetOfInputs, timesliceIndex.getOldestPossibleOutput(), true, action.op == CompletionPolicy::CompletionOp::Consume); } + markInputsAsDone(action.slot); uint64_t tStart = uv_hrtime(); @@ -2524,7 +2442,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v O2_SIGNPOST_EVENT_EMIT(device, pcid, "device", "Skipping processing because we are discarding."); } else { O2_SIGNPOST_EVENT_EMIT(device, pcid, "device", "No processing callback provided. Switching to %{public}s.", "Idle"); - switchState(ref, StreamingState::Idle); + DataProcessingHelpers::switchState(ref, StreamingState::Idle); } if (shouldProcess(action)) { auto& timingInfo = ref.get(); @@ -2593,7 +2511,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v context.postDispatchingCallbacks(processContext); ref.get().call(o2::framework::ServiceRegistryRef{ref}); } - if ((context.canForwardEarly == false) && hasForwards && consumeSomething) { + if ((context.forwardPolicy == ForwardPolicy::AfterProcessing) && hasForwards && consumeSomething) { O2_SIGNPOST_EVENT_EMIT(device, aid, "device", "Late forwarding"); auto& timesliceIndex = ref.get(); forwardInputs(ref, action.slot, currentSetOfInputs, timesliceIndex.getOldestPossibleOutput(), false, action.op == CompletionPolicy::CompletionOp::Consume); @@ -2612,7 +2530,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v for (auto& channel : spec.outputChannels) { DataProcessingHelpers::sendEndOfStream(ref, channel); } - switchState(ref, StreamingState::Idle); + DataProcessingHelpers::switchState(ref, StreamingState::Idle); } return true; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 58fda5a13bae9..334a0fc6045f6 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -16,14 +16,37 @@ #include "MemoryResources/MemoryResources.h" #include "Framework/FairMQDeviceProxy.h" #include "Headers/DataHeader.h" +#include "Headers/DataHeaderHelpers.h" #include "Headers/Stack.h" #include "Framework/Logger.h" #include "Framework/SendingPolicy.h" #include "Framework/RawDeviceService.h" +#include "Framework/DeviceState.h" +#include "Framework/DeviceContext.h" +#include "Framework/ProcessingPolicies.h" +#include "Framework/Signpost.h" +#include "Framework/CallbackService.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessingContext.h" +#include "Framework/DeviceStateEnums.h" +#include "Headers/DataHeader.h" +#include "Framework/DataProcessingHeader.h" +#include "DecongestionService.h" #include #include +#include + +// A log to use for general device logging +O2_DECLARE_DYNAMIC_LOG(device); +// Stream which keeps track of the calibration lifetime logic +O2_DECLARE_DYNAMIC_LOG(calibration); +O2_DECLARE_DYNAMIC_LOG(forwarding); + namespace o2::framework { void DataProcessingHelpers::sendEndOfStream(ServiceRegistryRef const& ref, OutputChannelSpec const& channel) @@ -61,6 +84,9 @@ void doSendOldestPossibleTimeframe(ServiceRegistryRef ref, fair::mq::TransportFa bool DataProcessingHelpers::sendOldestPossibleTimeframe(ServiceRegistryRef const& ref, ForwardChannelInfo const& info, ForwardChannelState& state, size_t timeslice) { + if (ref.get().suppressDomainInfo) { + return false; + } if (state.oldestForChannel.value >= timeslice) { return false; } @@ -71,6 +97,9 @@ bool DataProcessingHelpers::sendOldestPossibleTimeframe(ServiceRegistryRef const bool DataProcessingHelpers::sendOldestPossibleTimeframe(ServiceRegistryRef const& ref, OutputChannelInfo const& info, OutputChannelState& state, size_t timeslice) { + if (ref.get().suppressDomainInfo) { + return false; + } if (state.oldestForChannel.value >= timeslice) { return false; } @@ -89,4 +118,292 @@ void DataProcessingHelpers::broadcastOldestPossibleTimeslice(ServiceRegistryRef } } +void DataProcessingHelpers::switchState(ServiceRegistryRef const& ref, StreamingState newState) +{ + auto& state = ref.get(); + auto& context = ref.get(); + O2_SIGNPOST_ID_FROM_POINTER(dpid, device, &context); + O2_SIGNPOST_END(device, dpid, "state", "End of processing state %d", (int)state.streaming); + O2_SIGNPOST_START(device, dpid, "state", "Starting processing state %d", (int)newState); + state.streaming = newState; + ref.get().notifyStreamingState(state.streaming); +}; + +bool hasOnlyTimers(DeviceSpec const& spec) +{ + return std::all_of(spec.inputs.cbegin(), spec.inputs.cend(), [](InputRoute const& route) -> bool { return route.matcher.lifetime == Lifetime::Timer; }); +} + +bool DataProcessingHelpers::hasOnlyGenerated(DeviceSpec const& spec) +{ + return (spec.inputChannels.size() == 1) && (spec.inputs[0].matcher.lifetime == Lifetime::Timer || spec.inputs[0].matcher.lifetime == Lifetime::Enumeration); +} + +void on_data_processing_expired(uv_timer_t* handle) +{ + auto* ref = (ServiceRegistryRef*)handle->data; + auto& state = ref->get(); + auto& spec = ref->get(); + state.loopReason |= DeviceState::TIMER_EXPIRED; + + // Check if this is a source device + O2_SIGNPOST_ID_FROM_POINTER(cid, calibration, handle); + + if (DataProcessingHelpers::hasOnlyGenerated(spec)) { + O2_SIGNPOST_EVENT_EMIT_INFO(calibration, cid, "callback", "Grace period for data processing expired. Switching to EndOfStreaming."); + DataProcessingHelpers::switchState(*ref, StreamingState::EndOfStreaming); + } else { + O2_SIGNPOST_EVENT_EMIT_INFO(calibration, cid, "callback", "Grace period for data processing expired. Only calibrations from this point onwards."); + state.allowedProcessing = DeviceState::CalibrationOnly; + } +} + +void on_transition_requested_expired(uv_timer_t* handle) +{ + auto* ref = (ServiceRegistryRef*)handle->data; + auto& state = ref->get(); + state.loopReason |= DeviceState::TIMER_EXPIRED; + // Check if this is a source device + O2_SIGNPOST_ID_FROM_POINTER(cid, calibration, handle); + auto& spec = ref->get(); + std::string messageOnExpire = DataProcessingHelpers::hasOnlyGenerated(spec) ? "DPL exit transition grace period for source expired. Exiting." : fmt::format("DPL exit transition grace period for {} expired. Exiting.", state.allowedProcessing == DeviceState::CalibrationOnly ? "calibration" : "data & calibration").c_str(); + if (!ref->get().device()->GetConfig()->GetValue("error-on-exit-transition-timeout")) { + O2_SIGNPOST_EVENT_EMIT_WARN(calibration, cid, "callback", "%{public}s", messageOnExpire.c_str()); + } else { + O2_SIGNPOST_EVENT_EMIT_ERROR(calibration, cid, "callback", "%{public}s", messageOnExpire.c_str()); + } + state.transitionHandling = TransitionHandlingState::Expired; +} + +TransitionHandlingState DataProcessingHelpers::updateStateTransition(ServiceRegistryRef const& ref, ProcessingPolicies const& policies) +{ + auto& state = ref.get(); + auto& deviceProxy = ref.get(); + if (state.transitionHandling != TransitionHandlingState::NoTransition || deviceProxy.newStateRequested() == false) { + return state.transitionHandling; + } + O2_SIGNPOST_ID_FROM_POINTER(lid, device, state.loop); + O2_SIGNPOST_ID_FROM_POINTER(cid, calibration, state.loop); + auto& deviceContext = ref.get(); + // Check if we only have timers + auto& spec = ref.get(); + if (hasOnlyTimers(spec)) { + DataProcessingHelpers::switchState(ref, StreamingState::EndOfStreaming); + } + + // We do not do anything in particular if the data processing timeout would go past the exitTransitionTimeout + if (deviceContext.dataProcessingTimeout > 0 && deviceContext.dataProcessingTimeout < deviceContext.exitTransitionTimeout) { + uv_update_time(state.loop); + O2_SIGNPOST_EVENT_EMIT(calibration, cid, "timer_setup", "Starting %d s timer for dataProcessingTimeout.", deviceContext.dataProcessingTimeout); + uv_timer_start(deviceContext.dataProcessingGracePeriodTimer, on_data_processing_expired, deviceContext.dataProcessingTimeout * 1000, 0); + } + if (deviceContext.exitTransitionTimeout != 0 && state.streaming != StreamingState::Idle) { + ref.get().call(ServiceRegistryRef{ref}); + uv_update_time(state.loop); + O2_SIGNPOST_EVENT_EMIT(calibration, cid, "timer_setup", "Starting %d s timer for exitTransitionTimeout.", + deviceContext.exitTransitionTimeout); + uv_timer_start(deviceContext.gracePeriodTimer, on_transition_requested_expired, deviceContext.exitTransitionTimeout * 1000, 0); + bool onlyGenerated = DataProcessingHelpers::hasOnlyGenerated(spec); + int timeout = onlyGenerated ? deviceContext.dataProcessingTimeout : deviceContext.exitTransitionTimeout; + if (policies.termination == TerminationPolicy::QUIT && DefaultsHelpers::onlineDeploymentMode() == false) { + O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state requested. Waiting for %d seconds before quitting.", timeout); + } else { + O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", + "New state requested. Waiting for %d seconds before %{public}s", + timeout, + onlyGenerated ? "dropping remaining input and switching to READY state." : "switching to READY state."); + } + return TransitionHandlingState::Requested; + } else { + if (deviceContext.exitTransitionTimeout == 0 && policies.termination == TerminationPolicy::QUIT) { + O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state requested. No timeout set, quitting immediately as per --completion-policy"); + } else if (deviceContext.exitTransitionTimeout == 0 && policies.termination != TerminationPolicy::QUIT) { + O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state requested. No timeout set, switching to READY state immediately"); + } else if (policies.termination == TerminationPolicy::QUIT) { + O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state pending and we are already idle, quitting immediately as per --completion-policy"); + } else { + O2_SIGNPOST_EVENT_EMIT_INFO(device, lid, "run_loop", "New state pending and we are already idle, switching to READY immediately."); + } + return TransitionHandlingState::Expired; + } +} + +void DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, std::span& messages, std::vector& forwardedParts, + const bool copyByDefault, bool consume) +{ + O2_SIGNPOST_ID_GENERATE(sid, forwarding); + std::vector forwardingChoices{}; + size_t pi = 0; + while (pi < messages.size()) { + auto& header = messages[pi]; + + // If is now possible that the record is not complete when + // we forward it, because of a custom completion policy. + // this means that we need to skip the empty entries in the + // record for being forwarded. + if (header->GetData() == nullptr) { + pi += 2; + continue; + } + auto dih = o2::header::get(header->GetData()); + if (dih) { + pi += 2; + continue; + } + auto sih = o2::header::get(header->GetData()); + if (sih) { + pi += 2; + continue; + } + + auto dph = o2::header::get(header->GetData()); + auto dh = o2::header::get(header->GetData()); + + if (dph == nullptr || dh == nullptr) { + // Complain only if this is not an out-of-band message + LOGP(error, "Data is missing {}{}{}", + dph ? "DataProcessingHeader" : "", dph || dh ? "and" : "", dh ? "DataHeader" : ""); + pi += 2; + continue; + } + + // At least one payload. + auto& payload = messages[pi + 1]; + // Calculate the number of messages which should be handled together + // all in one go. + size_t numberOfMessages = 0; + if (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) { + // Sequence of (header, payload[0], ... , payload[splitPayloadParts - 1]) pairs belonging together. + numberOfMessages = dh->splitPayloadParts + 1; // one is for the header + } else { + // Sequence of splitPayloadParts (header, payload) pairs belonging together. + // In case splitPayloadParts = 0, we consider this as a single message pair + numberOfMessages = (dh->splitPayloadParts > 0 ? dh->splitPayloadParts : 1) * 2; + } + + if (payload.get() == nullptr && consume == true) { + // If the payload is not there, it means we already + // processed it with ConsumeExisiting. Therefore we + // need to do something only if this is the last consume. + header.reset(nullptr); + pi += numberOfMessages; + continue; + } + + // We need to find the forward route only for the first + // part of a split payload. All the others will use the same. + // Therefore, we reset and recompute the forwarding choice: + // + // - If this is the first payload of a [header0][payload0][header0][payload1]... sequence, + // which is actually always created and handled together. Notice that in this + // case we have splitPayloadParts == splitPayloadIndex + // - If this is the first payload of a [header0][payload0][header1][payload1]... sequence + // belonging to the same multipart message (and therefore we are guaranteed that they + // need to be routed together). + // - If the message is not a multipart (splitPayloadParts 0) or has only one part + // - If it's a message of the kind [header0][payload1][payload2][payload3]... and therefore + // we will already use the same choice in the for loop below. + // + + forwardingChoices.clear(); + proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime); + + if (forwardingChoices.empty()) { + // Nothing to forward go to the next messageset + pi += numberOfMessages; + continue; + } + + // In case of more than one forward route, we need to copy the message. + // This will eventually use the same memory if running with the same backend. + if (copyByDefault || forwardingChoices.size() > 1) { + for (auto& choice : forwardingChoices) { + O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding a copy of %{public}s to route %d.", + fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), choice.value); + + for (size_t ppi = pi; ppi < pi + numberOfMessages; ++ppi) { + auto&& newMsg = header->GetTransport()->CreateMessage(); + newMsg->Copy(*messages[ppi]); + forwardedParts[choice.value].AddPart(std::move(newMsg)); + } + } + } else { + O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding %{public}s to route %d.", + fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), forwardingChoices.back().value); + for (size_t ppi = pi; ppi < pi + numberOfMessages; ++ppi) { + forwardedParts[forwardingChoices.back().value].AddPart(std::move(messages[ppi])); + } + } + pi += numberOfMessages; + } +} + +void DataProcessingHelpers::cleanForwardedMessages(std::span& messages, bool consume) +{ + size_t pi = 0; + while (pi < messages.size()) { + auto& header = messages[pi]; + + // If is now possible that the record is not complete when + // we forward it, because of a custom completion policy. + // this means that we need to skip the empty entries in the + // record for being forwarded. + if (header->GetData() == nullptr || + o2::header::get(header->GetData()) || + o2::header::get(header->GetData())) { + pi += 2; + continue; + } + + auto dph = o2::header::get(header->GetData()); + auto dh = o2::header::get(header->GetData()); + + if (dph == nullptr || dh == nullptr) { + // Complain only if this is not an out-of-band message + LOGP(error, "Data is missing {}{}{}", + dph ? "DataProcessingHeader" : "", dph || dh ? "and" : "", dh ? "DataHeader" : ""); + pi += 2; + continue; + } + + // At least one payload. + auto& payload = messages[pi + 1]; + // Calculate the number of messages which should be handled together + // all in one go. + size_t numberOfMessages = 0; + if (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) { + // Sequence of (header, payload[0], ... , payload[splitPayloadParts - 1]) pairs belonging together. + numberOfMessages = dh->splitPayloadParts + 1; // one is for the header + } else { + // Sequence of splitPayloadParts (header, payload) pairs belonging together. + // In case splitPayloadParts = 0, we consider this as a single message pair + numberOfMessages = (dh->splitPayloadParts > 0 ? dh->splitPayloadParts : 1) * 2; + } + + if (payload.get() == nullptr && consume == true) { + // If the payload is not there, it means we already + // processed it with ConsumeExisiting. Therefore we + // need to do something only if this is the last consume. + header.reset(nullptr); + } + + // Nothing to forward go to the next messageset + pi += numberOfMessages; + } +} + +auto DataProcessingHelpers::routeForwardedMessageSet(FairMQDeviceProxy& proxy, + std::vector& currentSetOfInputs, + const bool copyByDefault, bool consume) -> std::vector +{ + // we collect all messages per forward in a map and send them together + std::vector forwardedParts(proxy.getNumForwardChannels()); + + for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { + auto span = std::span(currentSetOfInputs[ii].messages); + routeForwardedMessages(proxy, span, forwardedParts, copyByDefault, consume); + } + return forwardedParts; +}; + } // namespace o2::framework diff --git a/Framework/Core/src/DataProcessingStats.cxx b/Framework/Core/src/DataProcessingStats.cxx index 3b02a0aacdd70..8349af62acdc2 100644 --- a/Framework/Core/src/DataProcessingStats.cxx +++ b/Framework/Core/src/DataProcessingStats.cxx @@ -29,6 +29,16 @@ DataProcessingStats::DataProcessingStats(std::function& offers, + std::array& offers, ComputingQuotaStats& stats, std::function accountDisposed) { ComputingQuotaOffer disposed; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 092e8340a934a..cece5b343659f 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -37,6 +37,7 @@ #include "Framework/DataProcessingStates.h" #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/RawDeviceService.h" #include "Headers/DataHeaderHelpers.h" #include "Framework/Formatters.h" @@ -47,12 +48,11 @@ #include #include #include -#if __has_include() #include -#endif +#include #include #include -#include +#include #include using namespace o2::framework::data_matcher; @@ -72,7 +72,8 @@ constexpr int INVALID_INPUT = -1; DataRelayer::DataRelayer(const CompletionPolicy& policy, std::vector const& routes, TimesliceIndex& index, - ServiceRegistryRef services) + ServiceRegistryRef services, + int pipelineLength) : mContext{services}, mTimesliceIndex{index}, mCompletionPolicy{policy}, @@ -83,7 +84,17 @@ DataRelayer::DataRelayer(const CompletionPolicy& policy, std::scoped_lock lock(mMutex); if (policy.configureRelayer == nullptr) { - static int pipelineLength = DefaultsHelpers::pipelineLength(); + if (pipelineLength == -1) { + auto getPipelineLengthHelper = [&services]() { + try { + return DefaultsHelpers::pipelineLength(*services.get().device()->fConfig); + } catch (...) { + return DefaultsHelpers::pipelineLength(0); + } + }; + static int detectedPipelineLength = getPipelineLengthHelper(); + pipelineLength = detectedPipelineLength; + } setPipelineLength(pipelineLength); } else { policy.configureRelayer(*this); @@ -191,7 +202,7 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector gsl::span { + auto getPartialRecord = [&cache = mCache, numInputTypes = mDistinctRoutesIndex.size()](int li) -> std::span { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -215,14 +226,10 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector) auto refCountGetter = [&partial](size_t idx) -> int { auto& header = static_cast(*partial[idx].header(0)); return header.GetRefCount(); }; -#else - std::function refCountGetter = nullptr; -#endif InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; // Setup the input span @@ -442,7 +449,8 @@ DataRelayer::RelayChoice InputInfo const& info, size_t nMessages, size_t nPayloads, - std::function&, TimesliceIndex::OldestOutputInfo)> onDrop) + OnInsertionCallback onInsertion, + OnDropCallback onDrop) { std::scoped_lock lock(mMutex); DataProcessingHeader const* dph = o2::header::get(rawHeader); @@ -488,6 +496,7 @@ DataRelayer::RelayChoice &messages, &nMessages, &nPayloads, + &onInsertion, &cache = mCache, &services = mContext, numInputTypes = mDistinctRoutesIndex.size()](TimesliceId timeslice, int input, TimesliceSlot slot, InputInfo const& info) -> size_t { @@ -503,6 +512,12 @@ DataRelayer::RelayChoice // DataRelayer::relay assert(nPayloads > 0); size_t saved = 0; + // It's guaranteed we will see all these messages only once, so we can + // do the forwarding here. + auto allMessages = std::span(messages, messages + nMessages); + if (onInsertion) { + onInsertion(services, allMessages); + } for (size_t mi = 0; mi < nMessages; ++mi) { assert(mi + nPayloads < nMessages); // We are in calibration mode and the data does not have the calibration bit set. @@ -518,7 +533,10 @@ DataRelayer::RelayChoice mi += nPayloads; continue; } - target.add([&messages, &mi](size_t i) -> fair::mq::MessagePtr& { return messages[mi + i]; }, nPayloads + 1); + auto span = std::span(messages + mi, messages + mi + nPayloads + 1); + // Notice this will split [(header, payload), (header, payload)] multiparts + // in N different subParts for the message spec. + target.add([&span](size_t i) -> fair::mq::MessagePtr& { return span[i]; }, nPayloads + 1); mi += nPayloads; saved += nPayloads; } @@ -710,7 +728,7 @@ void DataRelayer::getReadyToProcess(std::vector& comp // // We use this to bail out early from the check as soon as we find something // which we know is not complete. - auto getPartialRecord = [&cache, &numInputTypes](int li) -> gsl::span { + auto getPartialRecord = [&cache, &numInputTypes](int li) -> std::span { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -781,14 +799,10 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto nPartsGetter = [&partial](size_t idx) { return partial[idx].size(); }; -#if __has_include() auto refCountGetter = [&partial](size_t idx) -> int { auto& header = static_cast(*partial[idx].header(0)); return header.GetRefCount(); }; -#else - std::function refCountGetter = nullptr; -#endif InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; CompletionPolicy::CompletionOp action = mCompletionPolicy.callbackFull(span, mInputs, mContext); @@ -1065,7 +1079,7 @@ void DataRelayer::sendContextState() char* buffer = relayerSlotState + written; for (size_t ci = 0; ci < mTimesliceIndex.size(); ++ci) { for (size_t si = 0; si < mDistinctRoutesIndex.size(); ++si) { - int index = si * mTimesliceIndex.size() + ci; + int index = ci * mDistinctRoutesIndex.size() + si; int value = static_cast(mCachedStateMetrics[index]); buffer[si] = value + '0'; // Anything which is done is actually already empty, diff --git a/Framework/Core/src/DataSpecUtils.cxx b/Framework/Core/src/DataSpecUtils.cxx index 48f5e6abcad5b..bc1fcd180ed76 100644 --- a/Framework/Core/src/DataSpecUtils.cxx +++ b/Framework/Core/src/DataSpecUtils.cxx @@ -89,6 +89,11 @@ std::string DataSpecUtils::describe(OutputSpec const& spec) spec.matcher); } +std::string DataSpecUtils::describe(ConcreteDataMatcher const& matcher) +{ + return join(matcher, "/"); +} + template size_t DataSpecUtils::describe(char* buffer, size_t size, T const& spec) { @@ -664,16 +669,39 @@ InputSpec DataSpecUtils::fromMetadataString(std::string s) if (std::distance(words, std::sregex_iterator()) != 4) { throw runtime_error_f("Malformed input spec metadata: %s", s.c_str()); } - std::vector data; + std::array data; + auto pos = 0; for (auto i = words; i != std::sregex_iterator(); ++i) { - data.emplace_back(i->str()); + data[pos] = i->str(); + ++pos; } char origin[4]; char description[16]; std::memcpy(&origin, data[1].c_str(), 4); std::memcpy(&description, data[2].c_str(), 16); auto version = static_cast(std::atoi(data[3].c_str())); - return InputSpec{data[0], header::DataOrigin{origin}, header::DataDescription{description}, version, Lifetime::Timeframe}; + return {data[0], header::DataOrigin{origin}, header::DataDescription{description}, version, Lifetime::Timeframe}; +} + +ConcreteDataMatcher DataSpecUtils::fromString(std::string s) +{ + std::regex word_regex("(\\w+)"); + auto words = std::sregex_iterator(s.begin(), s.end(), word_regex); + if (std::distance(words, std::sregex_iterator()) != 3) { + throw runtime_error_f("Malformed serialized matcher: %s", s.c_str()); + } + std::array data; + auto pos = 0; + for (auto i = words; i != std::sregex_iterator(); ++i) { + data[pos] = i->str(); + ++pos; + } + char origin[4]; + char description[16]; + std::memcpy(&origin, data[0].c_str(), 4); + std::memcpy(&description, data[1].c_str(), 16); + auto version = static_cast(std::atoi(data[2].c_str())); + return {header::DataOrigin{origin}, header::DataDescription{description}, version}; } std::optional DataSpecUtils::getOptionalOrigin(InputSpec const& spec) diff --git a/Framework/Core/src/DecongestionService.h b/Framework/Core/src/DecongestionService.h index c45e9a36217ec..1a42d3577bc0a 100644 --- a/Framework/Core/src/DecongestionService.h +++ b/Framework/Core/src/DecongestionService.h @@ -18,6 +18,8 @@ namespace o2::framework struct DecongestionService { /// Wether we are a source in the processing chain bool isFirstInTopology = true; + /// do not advertise/forward DomainInfoHeader from this device + bool suppressDomainInfo = false; /// The last timeslice which the ExpirationHandler::Creator callback /// created. This can be used to skip dummy iterations. size_t nextEnumerationTimeslice = 0; diff --git a/Framework/Core/src/DefaultsHelpers.cxx b/Framework/Core/src/DefaultsHelpers.cxx index 4dcc734216f0c..5fd1ed29e7af6 100644 --- a/Framework/Core/src/DefaultsHelpers.cxx +++ b/Framework/Core/src/DefaultsHelpers.cxx @@ -11,6 +11,9 @@ #include "Framework/DefaultsHelpers.h" #include "Framework/DataTakingContext.h" +#include "Framework/DeviceConfig.h" +#include + #include #include #include @@ -18,23 +21,35 @@ namespace o2::framework { -unsigned int DefaultsHelpers::pipelineLength() +unsigned int DefaultsHelpers::pipelineLength(unsigned int minLength) { static bool override = getenv("DPL_DEFAULT_PIPELINE_LENGTH"); if (override) { static unsigned int retval = atoi(getenv("DPL_DEFAULT_PIPELINE_LENGTH")); - return retval; + return std::max(minLength, retval); } DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); // just some reasonable numers // The number should really be tuned at runtime for each processor. if (deploymentMode == DeploymentMode::OnlineDDS || deploymentMode == DeploymentMode::OnlineECS || deploymentMode == DeploymentMode::FST) { - return 512; + return std::max(minLength, 512u); } else { - return 64; + return std::max(minLength, 64u); } } +unsigned int DefaultsHelpers::pipelineLength(const DeviceConfig& dc) +{ + static unsigned int minLength = dc.options.count("timeframes-rate-limit") ? std::max(0, atoi(dc.options["timeframes-rate-limit"].as().c_str())) : 0; + return pipelineLength(minLength); +} + +unsigned int DefaultsHelpers::pipelineLength(const fair::mq::ProgOptions& options) +{ + static unsigned int minLength = options.Count("timeframes-rate-limit") ? std::max(0, atoi(options.GetValue("timeframes-rate-limit").c_str())) : 0; + return pipelineLength(minLength); +} + static DeploymentMode getDeploymentMode_internal() { char* explicitMode = getenv("O2_DPL_DEPLOYMENT_MODE"); diff --git a/Framework/Core/src/DeviceSpecHelpers.cxx b/Framework/Core/src/DeviceSpecHelpers.cxx index ec0a40e44ac31..88e5269482ebd 100644 --- a/Framework/Core/src/DeviceSpecHelpers.cxx +++ b/Framework/Core/src/DeviceSpecHelpers.cxx @@ -1541,6 +1541,7 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, realOdesc.add_options()("child-driver", bpo::value()); realOdesc.add_options()("rate", bpo::value()); realOdesc.add_options()("exit-transition-timeout", bpo::value()); + realOdesc.add_options()("error-on-exit-transition-timeout", bpo::value()->zero_tokens()); realOdesc.add_options()("data-processing-timeout", bpo::value()); realOdesc.add_options()("expected-region-callbacks", bpo::value()); realOdesc.add_options()("timeframes-rate-limit", bpo::value()); @@ -1728,6 +1729,7 @@ boost::program_options::options_description DeviceSpecHelpers::getForwardedDevic ("control-port", bpo::value(), "Utility port to be used by O2 Control") // ("rate", bpo::value(), "rate for a data source device (Hz)") // ("exit-transition-timeout", bpo::value(), "timeout before switching to READY state") // + ("error-on-exit-transition-timeout", bpo::value()->zero_tokens(), "print error instead of warning when exit transition timer expires") // ("data-processing-timeout", bpo::value(), "timeout after which only calibration can happen") // ("expected-region-callbacks", bpo::value(), "region callbacks to expect before starting") // ("timeframes-rate-limit", bpo::value()->default_value("0"), "how many timeframes can be in fly") // diff --git a/Framework/Core/src/ExpressionJSONHelpers.cxx b/Framework/Core/src/ExpressionJSONHelpers.cxx new file mode 100644 index 0000000000000..a6e19875381cd --- /dev/null +++ b/Framework/Core/src/ExpressionJSONHelpers.cxx @@ -0,0 +1,875 @@ +// 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 "ExpressionJSONHelpers.h" + +#include +#include +#include +#include +#include + +#include +#include +#include "Framework/VariantHelpers.h" + +namespace o2::framework +{ +namespace +{ +using nodes = expressions::Node::self_t; +enum struct Nodes : int { + NLITERAL = 0, + NBINDING = 1, + NOP = 2, + NNPH = 3, + NCOND = 4, + NPAR = 5 +}; + +enum struct ToWrite { + FULL, + LEFT, + RIGHT, + COND, + POP +}; + +struct Entry { + expressions::Node* ptr = nullptr; + ToWrite toWrite = ToWrite::FULL; +}; + +std::array validKeys{ + "projectors", + "kind", + "binding", + "index", + "arrow_type", + "value", + "hash", + "operation", + "left", + "right", + "condition"}; + +struct ExpressionReader : public rapidjson::BaseReaderHandler, ExpressionReader> { + using Ch = rapidjson::UTF8<>::Ch; + using SizeType = rapidjson::SizeType; + + enum struct State { + IN_START, // global start + IN_LIST, // opening brace of the list + IN_ROOT, // after encountering the opening of the expression object + IN_LEFT, // in "left" key - subexpression + IN_RIGHT, // in "right" key - subexpression + IN_COND, // in "condition" key - subexpression + IN_NODE_LITERAL, // in literal node + IN_NODE_BINDING, // in binding node + IN_NODE_OP, // in operation node + IN_NODE_CONDITIONAL, // in conditional node + IN_ERROR // generic error state + }; + + std::stack states; + std::stack path; + std::ostringstream debug; + + std::vector result; + + std::unique_ptr rootNode = nullptr; + std::unique_ptr node = nullptr; + expressions::LiteralValue::stored_type value; + atype::type type; + Nodes kind; + std::string binding; + BasicOp operation; + uint32_t hash; + size_t index; + + std::string previousKey; + std::string currentKey; + + ExpressionReader() + { + debug << ">>> Start" << std::endl; + states.push(State::IN_START); + } + + bool StartArray() + { + debug << "StartArray()" << std::endl; + if (states.top() == State::IN_START) { + states.push(State::IN_LIST); + return true; + } + states.push(State::IN_ERROR); + return false; + } + + bool EndArray(SizeType) + { + debug << "EndArray()" << std::endl; + if (states.top() == State::IN_LIST) { + states.pop(); + return true; + } + states.push(State::IN_ERROR); + return false; + } + + bool Key(const Ch* str, SizeType, bool) + { + debug << "Key(" << str << ")" << std::endl; + previousKey = currentKey; + currentKey = str; + if (std::find(validKeys.begin(), validKeys.end(), currentKey) == validKeys.end()) { + states.push(State::IN_ERROR); + return false; + } + + if (states.top() == State::IN_START) { + if (currentKey.compare("projectors") == 0) { + return true; + } + } + + if (states.top() == State::IN_ROOT) { + if (currentKey.compare("kind") == 0) { + return true; + } else { + states.push(State::IN_ERROR); // should start from root node + return false; + } + } + + if (states.top() == State::IN_LEFT || states.top() == State::IN_RIGHT || states.top() == State::IN_COND) { + if (currentKey.compare("kind") == 0) { + return true; + } + } + + if (states.top() == State::IN_NODE_LITERAL || states.top() == State::IN_NODE_OP || states.top() == State::IN_NODE_BINDING || states.top() == State::IN_NODE_CONDITIONAL) { + if (currentKey.compare("index") == 0) { + return true; + } + if (currentKey.compare("left") == 0) { + // this is the point where the node header is parsed and we can create it + // create a new node instance here and set a pointer to it in a parent (current stack top), based on its state + // push the new node into the stack with LEFT state + switch (states.top()) { + case State::IN_NODE_LITERAL: + node = std::make_unique(expressions::LiteralNode{value, type}); + break; + case State::IN_NODE_BINDING: + node = std::make_unique(expressions::BindingNode{hash, type}, binding); + break; + case State::IN_NODE_OP: + node = std::make_unique(expressions::OpNode{operation}, expressions::LiteralNode{-1}); + break; + case State::IN_NODE_CONDITIONAL: + node = std::make_unique(expressions::ConditionalNode{}, expressions::LiteralNode{-1}, expressions::LiteralNode{-1}, expressions::LiteralNode{true}); + break; + default: + states.push(State::IN_ERROR); + return false; + } + + if (path.empty()) { + rootNode = std::move(node); + path.emplace(rootNode.get(), ToWrite::LEFT); + } else { + auto* n = path.top().ptr; + switch (path.top().toWrite) { + case ToWrite::LEFT: + n->left = std::move(node); + path.top().toWrite = ToWrite::RIGHT; + path.emplace(n->left.get(), ToWrite::LEFT); + break; + case ToWrite::RIGHT: + n->right = std::move(node); + path.top().toWrite = ToWrite::COND; + path.emplace(n->right.get(), ToWrite::LEFT); + break; + case ToWrite::COND: + n->condition = std::move(node); + path.pop(); + path.emplace(n->condition.get(), ToWrite::LEFT); + break; + default: + states.push(State::IN_ERROR); + return false; + } + } + + states.push(State::IN_LEFT); + return true; + } + if (currentKey.compare("right") == 0) { + if (states.top() == State::IN_LEFT) { + states.pop(); + } + // move the stack state of the node to RIGHT state + path.top().toWrite = ToWrite::RIGHT; + states.push(State::IN_RIGHT); + return true; + } + if (currentKey.compare("condition") == 0) { + if (states.top() == State::IN_RIGHT) { + states.pop(); + } + // move the stack state of the node to COND state + path.top().toWrite = ToWrite::COND; + states.push(State::IN_COND); + return true; + } + } + + if (states.top() == State::IN_NODE_LITERAL) { + if (currentKey.compare("arrow_type") == 0 || currentKey.compare("value") == 0) { + return true; + } + } + + if (states.top() == State::IN_NODE_BINDING) { + if (currentKey.compare("binding") == 0 || currentKey.compare("hash") == 0 || currentKey.compare("arrow_type") == 0) { + return true; + } + } + + if (states.top() == State::IN_NODE_OP) { + if (currentKey.compare("operation") == 0) { + return true; + } + } + + debug << ">>> Unrecognized" << std::endl; + states.push(State::IN_ERROR); + return false; + } + + bool StartObject() + { + // opening brace encountered + debug << "StartObject()" << std::endl; + // the first opening brace in the input + if (states.top() == State::IN_START) { + return true; + } + // the opening of an expression + if (states.top() == State::IN_LIST) { + states.push(State::IN_ROOT); + return true; + } + // if we are looking at subexpression + if (states.top() == State::IN_LEFT || states.top() == State::IN_RIGHT || states.top() == State::IN_COND) { // ready to start a new node + return true; + } + // no other object starts are expected + states.push(State::IN_ERROR); + return false; + } + + bool EndObject(SizeType) + { + // closing brace encountered + debug << "EndObject()" << std::endl; + // we are closing up an expression + if (states.top() == State::IN_NODE_LITERAL || states.top() == State::IN_NODE_OP || states.top() == State::IN_NODE_BINDING || states.top() == State::IN_NODE_CONDITIONAL) { // finalize node + // finalize the current node and pop it from the stack (the pointers should be already set + states.pop(); + // subexpression + if (states.top() == State::IN_LEFT || states.top() == State::IN_RIGHT || states.top() == State::IN_COND) { + states.pop(); + return true; + } + + // expression + if (states.top() == State::IN_ROOT) { + result.emplace_back(std::move(rootNode)); + states.pop(); + return true; + } + } + + // we are closing the list + if (states.top() == State::IN_START) { + return true; + } + // no other object ends are expectedd + states.push(State::IN_ERROR); + return false; + } + + bool Null() + { + // null value + debug << "Null()" << std::endl; + // the subexpression can be empty + if (states.top() == State::IN_LEFT || states.top() == State::IN_RIGHT || states.top() == State::IN_COND) { + // empty node, nothing to do + // move the path state to the next + if (path.top().toWrite == ToWrite::LEFT) { + path.top().toWrite = ToWrite::RIGHT; + } else if (path.top().toWrite == ToWrite::RIGHT) { + path.top().toWrite = ToWrite::COND; + } else if (path.top().toWrite == ToWrite::COND) { + path.pop(); + } + + states.pop(); + return true; + } + states.push(State::IN_ERROR); // no other contexts allow null + return false; + } + + bool Bool(bool b) + { + debug << "Bool(" << b << ")" << std::endl; + // can be a value in a literal node + if (states.top() == State::IN_NODE_LITERAL && currentKey.compare("value") == 0) { + value = b; + return true; + } + states.push(State::IN_ERROR); // no other contexts allow booleans + return false; + } + + bool Int(int i) + { + debug << "Int(" << i << ")" << std::endl; + // can be a value in a literal node + if (states.top() == State::IN_NODE_LITERAL && currentKey.compare("value") == 0) { // literal + switch (type) { + case atype::INT8: + value = (int8_t)i; + break; + case atype::INT16: + value = (int16_t)i; + break; + case atype::INT32: + value = i; + break; + case atype::UINT8: + value = (uint8_t)i; + break; + case atype::UINT16: + value = (uint16_t)i; + break; + case atype::UINT32: + value = (uint32_t)i; + break; + default: + states.push(State::IN_ERROR); + return false; + } + return true; + } + // can be a node kind designator + if (states.top() == State::IN_ROOT || states.top() == State::IN_LEFT || states.top() == State::IN_RIGHT || states.top() == State::IN_COND) { + if (currentKey.compare("kind") == 0) { + kind = (Nodes)i; + switch (kind) { + case Nodes::NLITERAL: + case Nodes::NNPH: + case Nodes::NPAR: { + states.push(State::IN_NODE_LITERAL); + debug << ">>> Literal node" << std::endl; + return true; + } + case Nodes::NBINDING: { + states.push(State::IN_NODE_BINDING); + debug << ">>> Binding node" << std::endl; + return true; + } + case Nodes::NOP: { + states.push(State::IN_NODE_OP); + debug << ">>> Operation node" << std::endl; + return true; + } + case Nodes::NCOND: { + states.push(State::IN_NODE_CONDITIONAL); + debug << ">>> Conditional node" << std::endl; + return true; + } + } + } + } + // can be node index + if (states.top() == State::IN_NODE_BINDING || states.top() == State::IN_NODE_CONDITIONAL || states.top() == State::IN_NODE_LITERAL || states.top() == State::IN_NODE_OP) { + if (currentKey.compare("index") == 0) { + index = (size_t)i; + return true; + } + } + // can be a node type designator + if (states.top() == State::IN_NODE_LITERAL || states.top() == State::IN_NODE_BINDING) { + if (currentKey.compare("arrow_type") == 0) { + type = (atype::type)i; + return true; + } + } + // can be a node operation designato + if (states.top() == State::IN_NODE_OP && currentKey.compare("operation") == 0) { + operation = (BasicOp)i; + return true; + } + states.push(State::IN_ERROR); // no other contexts allow ints + return false; + } + + bool Uint(unsigned i) + { + debug << "Uint(" << i << ")" << std::endl; + // can be node hash + if (states.top() == State::IN_NODE_BINDING && currentKey.compare("hash") == 0) { + hash = i; + return true; + } + // any positive value will be first read as unsigned, however the actual type is determined by node's arrow_type + debug << ">> falling back to Int" << std::endl; + return Int(i); + } + + bool Int64(int64_t i) + { + debug << "Int64(" << i << ")" << std::endl; + // can only be a literal node value + if (states.top() == State::IN_NODE_LITERAL && currentKey.compare("value") == 0) { + switch (type) { + case atype::UINT64: + value = (uint64_t)i; + break; + case atype::INT64: + value = (int64_t)i; + break; + default: + states.push(State::IN_ERROR); + return false; + } + return true; + } + states.push(State::IN_ERROR); // no other contexts allow int64s + return false; + } + + bool Uint64(uint64_t i) + { + debug << "Uint64(" << i << ")" << std::endl; + // any positive value will be first read as unsigned, however the actual type is determined by node's arrow_type + debug << ">> falling back to Int64" << std::endl; + return Int64(i); + } + + bool Double(double d) + { + debug << "Double(" << d << ")" << std::endl; + // can only be a literal node value + if (states.top() == State::IN_NODE_LITERAL) { + switch (type) { + case atype::FLOAT: + value = (float)d; + break; + case atype::DOUBLE: + value = d; + break; + default: + states.push(State::IN_ERROR); + return false; + } + return true; + } + states.push(State::IN_ERROR); // no other contexts allow doubles + return false; + } + + bool String(const Ch* str, SizeType, bool) + { + debug << "String(" << str << ")" << std::endl; + // can only be a binding node + if (states.top() == State::IN_NODE_BINDING && currentKey.compare("binding") == 0) { + binding = str; + return true; + } + states.push(State::IN_ERROR); // no strings are expected + return false; + } +}; +} // namespace + +std::vector o2::framework::ExpressionJSONHelpers::read(std::istream& s) +{ + rapidjson::Reader reader; + rapidjson::IStreamWrapper isw(s); + ExpressionReader ereader; + bool ok = reader.Parse(isw, ereader); + + if (!ok) { + throw framework::runtime_error_f("Cannot parse serialized Expression, error: %s at offset: %d", rapidjson::GetParseError_En(reader.GetParseErrorCode()), reader.GetErrorOffset()); + } + return std::move(ereader.result); +} + +namespace +{ +void writeNodeHeader(rapidjson::Writer& w, expressions::Node const* node) +{ + w.Key("kind"); + w.Int((int)node->self.index()); + w.Key("index"); + w.Uint64(node->index); + std::visit(overloaded{ + [&w](expressions::LiteralNode const& node) { + w.Key("arrow_type"); + w.Int(node.type); + w.Key("value"); + std::visit(overloaded{ + [&w](bool v) { w.Bool(v); }, + [&w](float v) { w.Double(v); }, + [&w](double v) { w.Double(v); }, + [&w](uint8_t v) { w.Uint(v); }, + [&w](uint16_t v) { w.Uint(v); }, + [&w](uint32_t v) { w.Uint(v); }, + [&w](uint64_t v) { w.Uint64(v); }, + [&w](int8_t v) { w.Int(v); }, + [&w](int16_t v) { w.Int(v); }, + [&w](int v) { w.Int(v); }, + [&w](int64_t v) { w.Int64(v); }}, + node.value); + }, + [&w](expressions::BindingNode const& node) { + w.Key("binding"); + w.String(node.name); + w.Key("hash"); + w.Uint(node.hash); + w.Key("arrow_type"); + w.Int(node.type); + }, + [&w](expressions::OpNode const& node) { + w.Key("operation"); + w.Int(node.op); + }, + [](expressions::ConditionalNode const&) { + }}, + node->self); +} + +void writeExpression(rapidjson::Writer& w, expressions::Node* n) +{ + std::stack path; + path.emplace(n, ToWrite::FULL); + while (!path.empty()) { + auto& top = path.top(); + + if (top.toWrite == ToWrite::FULL) { + w.StartObject(); + writeNodeHeader(w, top.ptr); + top.toWrite = ToWrite::LEFT; + continue; + } + + if (top.toWrite == ToWrite::LEFT) { + w.Key("left"); + top.toWrite = ToWrite::RIGHT; + auto* left = top.ptr->left.get(); + if (left != nullptr) { + path.emplace(left, ToWrite::FULL); + } else { + w.Null(); + } + continue; + } + + if (top.toWrite == ToWrite::RIGHT) { + w.Key("right"); + top.toWrite = ToWrite::COND; + auto* right = top.ptr->right.get(); + if (right != nullptr) { + path.emplace(right, ToWrite::FULL); + } else { + w.Null(); + } + continue; + } + + if (top.toWrite == ToWrite::COND) { + w.Key("condition"); + top.toWrite = ToWrite::POP; + auto* cond = top.ptr->condition.get(); + if (cond != nullptr) { + path.emplace(cond, ToWrite::FULL); + } else { + w.Null(); + } + continue; + } + + if (top.toWrite == ToWrite::POP) { + w.EndObject(); + path.pop(); + continue; + } + } +} +} // namespace + +void o2::framework::ExpressionJSONHelpers::write(std::ostream& o, std::vector& projectors) +{ + rapidjson::OStreamWrapper osw(o); + rapidjson::Writer w(osw); + w.StartObject(); + w.Key("projectors"); + w.StartArray(); + for (auto& p : projectors) { + writeExpression(w, p.node.get()); + } + w.EndArray(); + w.EndObject(); +} + +namespace +{ +std::shared_ptr arrowDataTypeFromId(atype::type type, int list_size = 1, atype::type element = atype::NA) +{ + switch (list_size) { + case -1: + return arrow::list(expressions::concreteArrowType(element)); + case 1: + return expressions::concreteArrowType(type); + default: + return arrow::fixed_size_list(expressions::concreteArrowType(element), list_size); + } +} + +struct SchemaReader : public rapidjson::BaseReaderHandler, SchemaReader> { + using Ch = rapidjson::UTF8<>::Ch; + using SizeType = rapidjson::SizeType; + + enum struct State { + IN_START, + IN_LIST, + IN_FIELD, + IN_ERROR + }; + + std::stack states; + std::ostringstream debug; + + std::shared_ptr schema = nullptr; + std::vector> fields; + + std::string currentKey; + + std::string name; + atype::type type; + atype::type element; + int list_size = 1; + + SchemaReader() + { + debug << ">>> Start" << std::endl; + states.push(State::IN_START); + } + + bool StartArray() + { + debug << "StartArray()" << std::endl; + if (states.top() == State::IN_START && currentKey.compare("fields") == 0) { + states.push(State::IN_LIST); + return true; + } + states.push(State::IN_ERROR); + return false; + } + + bool EndArray(SizeType) + { + debug << "EndArray()" << std::endl; + if (states.top() == State::IN_LIST) { + // finalize schema + schema = std::make_shared(fields); + states.pop(); + return true; + } + states.push(State::IN_ERROR); + return false; + } + + bool Key(const Ch* str, SizeType, bool) + { + debug << "Key(" << str << ")" << std::endl; + currentKey = str; + if (states.top() == State::IN_START) { + if (currentKey.compare("fields") == 0) { + return true; + } + } + + if (states.top() == State::IN_FIELD) { + if (currentKey.compare("name") == 0) { + return true; + } + if (currentKey.compare("type") == 0) { + return true; + } + if (currentKey.compare("size") == 0) { + return true; + } + if (currentKey.compare("element") == 0) { + return true; + } + } + + states.push(State::IN_ERROR); + return false; + } + + bool StartObject() + { + debug << "StartObject()" << std::endl; + if (states.top() == State::IN_START) { + return true; + } + + if (states.top() == State::IN_LIST) { + states.push(State::IN_FIELD); + list_size = 1; + element = atype::NA; + type = atype::NA; + return true; + } + + states.push(State::IN_ERROR); + return false; + } + + bool EndObject(SizeType) + { + debug << "EndObject()" << std::endl; + if (states.top() == State::IN_FIELD) { + states.pop(); + // add a field + fields.emplace_back(std::make_shared(name, arrowDataTypeFromId(type, list_size, element))); + return true; + } + + if (states.top() == State::IN_START) { + return true; + } + + states.push(State::IN_ERROR); + return false; + } + + bool Uint(unsigned i) + { + debug << "Uint(" << i << ")" << std::endl; + if (states.top() == State::IN_FIELD) { + if (currentKey.compare("type") == 0) { + type = (atype::type)i; + return true; + } + if (currentKey.compare("element") == 0) { + element = (atype::type)i; + return true; + } + if (currentKey.compare("size") == 0) { + list_size = i; + return true; + } + } + + states.push(State::IN_ERROR); + return false; + } + + bool String(const Ch* str, SizeType, bool) + { + debug << "String(" << str << ")" << std::endl; + if (states.top() == State::IN_FIELD) { + if (currentKey.compare("name") == 0) { + name = str; + return true; + } + } + + states.push(State::IN_ERROR); + return false; + } + + bool Int(int i) + { + debug << "Int(" << i << ")" << std::endl; + if (states.top() == State::IN_FIELD && currentKey.compare("size") == 0) { + list_size = i; + return true; + } + return Uint(i); + } +}; +} // namespace + +std::shared_ptr o2::framework::ArrowJSONHelpers::read(std::istream& s) +{ + rapidjson::Reader reader; + rapidjson::IStreamWrapper isw(s); + SchemaReader sreader; + + bool ok = reader.Parse(isw, sreader); + + if (!ok) { + throw framework::runtime_error_f("Cannot parse serialized Schema, error: %s at offset: %d", rapidjson::GetParseError_En(reader.GetParseErrorCode()), reader.GetErrorOffset()); + } + return sreader.schema; +} + +namespace +{ +void writeSchema(rapidjson::Writer& w, arrow::Schema* schema) +{ + for (auto& f : schema->fields()) { + w.StartObject(); + w.Key("name"); + w.String(f->name().c_str()); + auto fixedList = dynamic_cast(f->type().get()); + if (fixedList != nullptr) { + w.Key("size"); + w.Int(fixedList->list_size()); + w.Key("element"); + w.Int(fixedList->field(0)->type()->id()); + } + auto varList = dynamic_cast(f->type().get()); + if (varList != nullptr) { + w.Key("size"); + w.Int(-1); + w.Key("element"); + w.Int(varList->field(0)->type()->id()); + } + w.Key("type"); + w.Int(f->type()->id()); + w.EndObject(); + } +} +} // namespace + +void o2::framework::ArrowJSONHelpers::write(std::ostream& o, std::shared_ptr& schema) +{ + rapidjson::OStreamWrapper osw(o); + rapidjson::Writer w(osw); + w.StartObject(); + w.Key("fields"); + w.StartArray(); + writeSchema(w, schema.get()); + w.EndArray(); + w.EndObject(); +} + +} // namespace o2::framework diff --git a/Framework/Core/src/ExpressionJSONHelpers.h b/Framework/Core/src/ExpressionJSONHelpers.h new file mode 100644 index 0000000000000..ed4c51c58d5c2 --- /dev/null +++ b/Framework/Core/src/ExpressionJSONHelpers.h @@ -0,0 +1,29 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef FRAMEWORK_EXPRESSIONJSONHELPERS_H +#define FRAMEWORK_EXPRESSIONJSONHELPERS_H + +#include "Framework/Expressions.h" + +namespace o2::framework +{ +struct ExpressionJSONHelpers { + static std::vector read(std::istream& s); + static void write(std::ostream& o, std::vector& projectors); +}; + +struct ArrowJSONHelpers { + static std::shared_ptr read(std::istream& s); + static void write(std::ostream& o, std::shared_ptr& schema); +}; +} // namespace o2::framework + +#endif // FRAMEWORK_EXPRESSIONJSONHELPERS_H diff --git a/Framework/Core/src/Expressions.cxx b/Framework/Core/src/Expressions.cxx index 05a3462d6e4da..02a862d30032b 100644 --- a/Framework/Core/src/Expressions.cxx +++ b/Framework/Core/src/Expressions.cxx @@ -1348,4 +1348,20 @@ OpNode Parser::opFromToken(std::string const& token) return OpNode{static_cast(std::distance(mapping.begin(), locate))}; } +std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> const& outputFields) +{ + std::vector> expressions; + int i = 0; + for (auto const& p : projectors) { + expressions.push_back( + expressions::makeExpression( + expressions::createExpressionTree( + expressions::createOperations(p), + inputSchema), + outputFields[i])); + ++i; + } + return expressions; +} + } // namespace o2::framework::expressions diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index c07baeaaf40b8..3b0275879a158 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -32,6 +32,7 @@ #include "Framework/DeviceState.h" #include "Framework/Monitoring.h" #include "Framework/SendingPolicy.h" +#include "Framework/DataProcessingHelpers.h" #include "Headers/DataHeader.h" #include "Headers/Stack.h" #include "DecongestionService.h" @@ -864,6 +865,10 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, bool didSendParts = false; for (size_t ci = 0; ci < channels.size(); ++ci) { + // check for state transition request every 10th input channel to avoid large delays of EoS timers + if (ci > 0 && ci % 10 == 0) { + ctx.services().get().transitionHandling = DataProcessingHelpers::updateStateTransition(ctx.services(), ctx.services().get().processingPolicies); + } std::string const& channel = channels[ci]; int waitTime = channels.size() == 1 ? -1 : 1; int maxRead = 1000; @@ -1040,6 +1045,7 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, spec.options = { ConfigParamSpec{"channel-config", VariantType::String, d, {"Out-of-band channel config"}}, }; + spec.labels.push_back(DataProcessorLabel{"output-proxy"}); return spec; } @@ -1085,7 +1091,18 @@ DataProcessorSpec specifyFairMQDeviceMultiOutputProxy(char const* name, channelNames->emplace_back(std::move(channel)); } - proxy.bind(mutableDeviceSpec.outputs, mutableDeviceSpec.inputs, mutableDeviceSpec.forwards, *device); + std::function bindByName = [device](std::string const& channelName) -> fair::mq::Channel& { + auto channel = device->GetChannels().find(channelName); + if (channel == device->GetChannels().end()) { + LOGP(fatal, "Expected channel {} not configured.", channelName); + } + return channel->second.at(0); + }; + + std::function newStateCallback = [device]() -> bool { + return device->NewStatePending(); + }; + proxy.bind(mutableDeviceSpec.outputs, mutableDeviceSpec.inputs, mutableDeviceSpec.forwards, bindByName, newStateCallback); }; // We need to clear the channels on stop, because we will check and add them auto channelConfigurationDisposer = [&deviceSpec]() { @@ -1164,6 +1181,7 @@ DataProcessorSpec specifyFairMQDeviceMultiOutputProxy(char const* name, spec.options = { ConfigParamSpec{"channel-config", VariantType::String, d, {"Out-of-band channel config"}}, }; + spec.labels.push_back(DataProcessorLabel{"output-proxy"}); return spec; } diff --git a/Framework/Core/src/FairMQDeviceProxy.cxx b/Framework/Core/src/FairMQDeviceProxy.cxx index bdffddd5a4d1a..e121084b866a2 100644 --- a/Framework/Core/src/FairMQDeviceProxy.cxx +++ b/Framework/Core/src/FairMQDeviceProxy.cxx @@ -230,7 +230,8 @@ std::unique_ptr FairMQDeviceProxy::createForwardMessage(Route void FairMQDeviceProxy::bind(std::vector const& outputs, std::vector const& inputs, std::vector const& forwards, - fair::mq::Device& device) + std::function bindChannelByName, + std::function newStatePending) { mOutputs.clear(); mOutputRoutes.clear(); @@ -258,14 +259,11 @@ void FairMQDeviceProxy::bind(std::vector const& outputs, std::vecto if (channelPos == channelNameToChannel.end()) { channelIndex = ChannelIndex{(int)mOutputChannelInfos.size()}; ChannelAccountingType dplChannel = (route.channel.rfind("from_", 0) == 0) ? ChannelAccountingType::DPL : ChannelAccountingType::RAWFMQ; - auto channel = device.GetChannels().find(route.channel); - if (channel == device.GetChannels().end()) { - LOGP(fatal, "Expected channel {} not configured.", route.channel); - } + auto& channel = bindChannelByName(route.channel); OutputChannelInfo info{ .name = route.channel, .channelType = dplChannel, - .channel = channel->second.at(0), + .channel = channel, .policy = route.policy, .index = channelIndex, }; @@ -305,11 +303,9 @@ void FairMQDeviceProxy::bind(std::vector const& outputs, std::vecto if (channelPos == channelNameToChannel.end()) { channelIndex = ChannelIndex{(int)mInputChannels.size()}; - auto channel = device.GetChannels().find(route.sourceChannel); - if (channel == device.GetChannels().end()) { - LOGP(fatal, "Expected channel {} not configured.", route.sourceChannel); - } - mInputChannels.push_back(&channel->second.at(0)); + fair::mq::Channel& channel = bindChannelByName(route.sourceChannel); + + mInputChannels.push_back(&channel); mInputChannelNames.push_back(route.sourceChannel); channelNameToChannel[route.sourceChannel] = channelIndex; LOGP(detail, "Binding channel {} to channel index {}", route.sourceChannel, channelIndex.value); @@ -341,12 +337,10 @@ void FairMQDeviceProxy::bind(std::vector const& outputs, std::vecto if (channelPos == channelNameToChannel.end()) { channelIndex = ChannelIndex{(int)mForwardChannelInfos.size()}; - auto channel = device.GetChannels().find(route.channel); - if (channel == device.GetChannels().end()) { - LOGP(fatal, "Expected channel {} not configured.", route.channel); - } + auto& channel = bindChannelByName(route.channel); + ChannelAccountingType dplChannel = (route.channel.rfind("from_", 0) == 0) ? ChannelAccountingType::DPL : ChannelAccountingType::RAWFMQ; - mForwardChannelInfos.push_back(ForwardChannelInfo{.name = route.channel, .channelType = dplChannel, .channel = channel->second.at(0), .policy = route.policy, .index = channelIndex}); + mForwardChannelInfos.push_back(ForwardChannelInfo{.name = route.channel, .channelType = dplChannel, .channel = channel, .policy = route.policy, .index = channelIndex}); mForwardChannelStates.push_back(ForwardChannelState{0}); channelNameToChannel[route.channel] = channelIndex; LOGP(detail, "Binding forward channel {} to channel index {}", route.channel, channelIndex.value); @@ -368,6 +362,6 @@ void FairMQDeviceProxy::bind(std::vector const& outputs, std::vecto LOGP(detail, "Forward route {}@{}%{} to index {} and channelIndex {}", DataSpecUtils::describe(route.matcher), route.timeslice, route.maxTimeslices, fi, state.channel.value); } } - mStateChangeCallback = [&device]() -> bool { return device.NewStatePending(); }; + mStateChangeCallback = newStatePending; } } // namespace o2::framework diff --git a/Framework/Core/src/HistogramRegistry.cxx b/Framework/Core/src/HistogramRegistry.cxx index 5e39fbe7181e7..9caa7cbd1f48e 100644 --- a/Framework/Core/src/HistogramRegistry.cxx +++ b/Framework/Core/src/HistogramRegistry.cxx @@ -55,7 +55,8 @@ OutputRef HistogramRegistry::ref(uint16_t pipelineIndex, uint16_t pipelineSize) { OutputObjHeader header{mPolicy, OutputObjSourceType::HistogramRegistrySource, mTaskHash, pipelineIndex, pipelineSize}; // Copy the name of the registry to the haeder. - strncpy(header.containerName, mName.data(), 64); + strncpy(header.containerName, mName.data(), OutputObjHeader::MAX_REGISTRY_NAME_SIZE); + header.createContainer = mCreateRegistryDir ? 1 : 0; return OutputRef{std::string{mName}, 0, o2::header::Stack{header}}; } diff --git a/Framework/Core/src/IndexBuilderHelpers.cxx b/Framework/Core/src/IndexBuilderHelpers.cxx index 52d6080690fe1..0943dea42169c 100644 --- a/Framework/Core/src/IndexBuilderHelpers.cxx +++ b/Framework/Core/src/IndexBuilderHelpers.cxx @@ -12,6 +12,7 @@ #include "Framework/RuntimeError.h" #include "Framework/IndexBuilderHelpers.h" #include "Framework/CompilerBuiltins.h" +#include "Framework/VariantHelpers.h" #include #include #include @@ -22,130 +23,87 @@ namespace o2::framework { void cannotBuildAnArray() { - throw runtime_error("Cannot build an array"); + throw framework::runtime_error("Cannot finish an array"); +} + +void cannotCreateIndexBuilder() +{ + throw framework::runtime_error("Cannot create index column builder: invalid kind of index column"); } ChunkedArrayIterator::ChunkedArrayIterator(std::shared_ptr source) - : mSource{source} + : mSource{source}, + mSourceSize{(size_t)source->length()} { mCurrentArray = getCurrentArray(); mCurrent = reinterpret_cast(mCurrentArray->values()->data()) + mOffset; mLast = mCurrent + mCurrentArray->length(); } -SelfIndexColumnBuilder::SelfIndexColumnBuilder(const char* name, arrow::MemoryPool* pool) - : mColumnName{name}, - mArrowType{arrow::int32()} +void ChunkedArrayIterator::reset(std::shared_ptr& source) { - auto status = arrow::MakeBuilder(pool, arrow::int32(), &mBuilder); - if (!status.ok()) { - throw runtime_error("Cannot create array builder!"); - } -} + mPosition = 0; + mChunk = 0; + mOffset = 0; + mCurrentArray = nullptr; + mCurrent = nullptr; + mLast = nullptr; + mFirstIndex = 0; + mSourceSize = 0; -std::shared_ptr SelfIndexColumnBuilder::field() const -{ - return std::make_shared(mColumnName, mArrowType); + mSource = source; + mSourceSize = (size_t)source->length(); + mCurrentArray = getCurrentArray(); + mCurrent = reinterpret_cast(mCurrentArray->values()->data()) + mOffset; + mLast = mCurrent + mCurrentArray->length(); } -IndexColumnBuilder::IndexColumnBuilder(std::shared_ptr source, const char* name, int listSize, arrow::MemoryPool* pool) - : SelfIndexColumnBuilder{name, pool}, - ChunkedArrayIterator{source}, - mListSize{listSize}, - mSourceSize{(size_t)source->length()} +SelfBuilder::SelfBuilder(arrow::MemoryPool* pool) { - switch (mListSize) { - case 1: { - mValueBuilder = mBuilder.get(); - mArrowType = arrow::int32(); - }; break; - case 2: { - if (preSlice().ok()) { - mListBuilder = std::make_unique(pool, std::move(mBuilder), mListSize); - mValueBuilder = static_cast(mListBuilder.get())->value_builder(); - mArrowType = arrow::fixed_size_list(arrow::int32(), 2); - } else { - throw runtime_error("Cannot pre-slice an array"); - } - }; break; - case -1: { - if (preFind().ok()) { - mListBuilder = std::make_unique(pool, std::move(mBuilder)); - mValueBuilder = static_cast(mListBuilder.get())->value_builder(); - mArrowType = arrow::list(arrow::int32()); - } else { - throw runtime_error("Cannot pre-find array groups"); - } - }; break; - default: - throw runtime_error_f("Invalid list size for index column: %d", mListSize); + auto status = arrow::MakeBuilder(pool, arrow::int32(), &mBuilder); + if (!status.ok()) { + throw framework::runtime_error("Cannot create array builder for the self-index!"); } } - -arrow::Status IndexColumnBuilder::preSlice() +// static_cast(this)->reset(pool); +void SelfBuilder::reset(std::shared_ptr) { - arrow::Datum value_counts; - auto options = arrow::compute::ScalarAggregateOptions::Defaults(); - ARROW_ASSIGN_OR_RAISE(value_counts, arrow::compute::CallFunction("value_counts", {mSource}, &options)); - auto pair = static_cast(value_counts.array()); - mValuesArrow = std::make_shared>(pair.field(0)->data()); - mCounts = std::make_shared>(pair.field(1)->data()); - return arrow::Status::OK(); + mBuilder->Reset(); + keyIndex = nullptr; } -arrow::Status IndexColumnBuilder::preFind() +void SelfBuilder::fill(int idx) { - arrow::Datum max; - auto options = arrow::compute::ScalarAggregateOptions::Defaults(); - ARROW_ASSIGN_OR_RAISE(max, arrow::compute::CallFunction("max", {mSource}, &options)); - auto maxValue = std::dynamic_pointer_cast(max.scalar())->value; - mIndices.resize(maxValue + 1); - - auto row = 0; - for (auto i = 0; i < mSource->length(); ++i) { - auto v = valueAt(i); - if (v >= 0) { - mValues.emplace_back(v); - mIndices[v].push_back(row); - } - ++row; - } - std::sort(mValues.begin(), mValues.end()); - - return arrow::Status::OK(); + (void)static_cast(mBuilder.get())->Append(idx); } -std::shared_ptr IndexColumnBuilder::resultSingle() const +std::shared_ptr SelfBuilder::result() const { std::shared_ptr array; - auto status = static_cast(mValueBuilder)->Finish(&array); + auto status = static_cast(mBuilder.get())->Finish(&array); if (!status.ok()) { - throw runtime_error("Cannot build an array"); + cannotBuildAnArray(); } + return std::make_shared(array); } -std::shared_ptr IndexColumnBuilder::resultSlice() const +SingleBuilder::SingleBuilder(std::shared_ptr source, arrow::MemoryPool* pool) + : ChunkedArrayIterator{source} { - std::shared_ptr array; - auto status = static_cast(mListBuilder.get())->Finish(&array); + auto status = arrow::MakeBuilder(pool, arrow::int32(), &mBuilder); if (!status.ok()) { - throw runtime_error("Cannot build an array"); + throw framework::runtime_error("Cannot create array builder for the single-valued index!"); } - return std::make_shared(array); } -std::shared_ptr IndexColumnBuilder::resultMulti() const +void SingleBuilder::reset(std::shared_ptr source) { - std::shared_ptr array; - auto status = static_cast(mListBuilder.get())->Finish(&array); - if (!status.ok()) { - throw runtime_error("Cannot build an array"); - } - return std::make_shared(array); + static_cast(this)->reset(source); + mBuilder->Reset(); } -bool IndexColumnBuilder::findSingle(int idx) +bool SingleBuilder::find(int idx) { auto count = mSourceSize - mPosition; while (count > 0) { @@ -166,13 +124,60 @@ bool IndexColumnBuilder::findSingle(int idx) return (mPosition < mSourceSize && valueAt(mPosition) == idx); } -bool IndexColumnBuilder::findSlice(int idx) +void SingleBuilder::fill(int idx) { - auto count = mValuesArrow->length() - mValuePos; + if (mPosition < mSourceSize && valueAt(mPosition) == idx) { + (void)static_cast(mBuilder.get())->Append((int)mPosition); + } else { + (void)static_cast(mBuilder.get())->Append(-1); + } +} + +std::shared_ptr SingleBuilder::result() const +{ + std::shared_ptr array; + auto status = static_cast(mBuilder.get())->Finish(&array); + if (!status.ok()) { + cannotBuildAnArray(); + } + return std::make_shared(array); +} + +SliceBuilder::SliceBuilder(std::shared_ptr source, arrow::MemoryPool* pool) + : ChunkedArrayIterator{source} +{ + if (!preSlice().ok()) { + throw framework::runtime_error("Cannot pre-slice the source for slice-index building"); + } + + std::unique_ptr builder; + auto status = arrow::MakeBuilder(pool, arrow::int32(), &builder); + if (!status.ok()) { + throw framework::runtime_error("Cannot create array for the slice-index builder!"); + } + mListBuilder = std::make_unique(pool, std::move(builder), 2); + mValueBuilder = static_cast(mListBuilder.get())->value_builder(); +} + +void SliceBuilder::reset(std::shared_ptr source) +{ + mValues = nullptr; + mCounts = nullptr; + mListBuilder->Reset(); + mValuePos = 0; + static_cast(this)->reset(source); + if (!preSlice().ok()) { + throw framework::runtime_error("Cannot pre-slice the source for slice-index building"); + } +} + +bool SliceBuilder::find(int idx) +{ + auto count = mValues->length() - mValuePos; while (count > 0) { auto step = count / 2; mValuePos += step; - if (mValuesArrow->Value(mValuePos) <= idx) { + if (mValues->Value(mValuePos) <= idx) { count -= step + 1; } else { mValuePos -= step; @@ -180,32 +185,17 @@ bool IndexColumnBuilder::findSlice(int idx) } } - if (mValuePos < mValuesArrow->length() && mValuesArrow->Value(mValuePos) <= idx) { + if (mValuePos < mValues->length() && mValues->Value(mValuePos) <= idx) { ++mPosition; } - return (mValuePos < mValuesArrow->length() && mValuesArrow->Value(mValuePos) == idx); -} - -bool IndexColumnBuilder::findMulti(int idx) -{ - return (std::find(mValues.begin(), mValues.end(), idx) != mValues.end()); -} - -void IndexColumnBuilder::fillSingle(int idx) -{ - // entry point - if (mPosition < mSourceSize && valueAt(mPosition) == idx) { - (void)static_cast(mValueBuilder)->Append((int)mPosition); - } else { - (void)static_cast(mValueBuilder)->Append(-1); - } + return (mValuePos < mValues->length() && mValues->Value(mValuePos) == idx); } -void IndexColumnBuilder::fillSlice(int idx) +void SliceBuilder::fill(int idx) { int data[2] = {-1, -1}; - if (mValuePos < mValuesArrow->length() && mValuesArrow->Value(mValuePos) == idx) { + if (mValuePos < mValues->length() && mValues->Value(mValuePos) == idx) { for (auto i = 0; i < mValuePos; ++i) { data[0] += mCounts->Value(i); } @@ -216,7 +206,60 @@ void IndexColumnBuilder::fillSlice(int idx) (void)static_cast(mValueBuilder)->AppendValues(data, 2); } -void IndexColumnBuilder::fillMulti(int idx) +std::shared_ptr SliceBuilder::result() const +{ + std::shared_ptr array; + auto status = static_cast(mListBuilder.get())->Finish(&array); + if (!status.ok()) { + cannotBuildAnArray(); + } + return std::make_shared(array); +} + +arrow::Status SliceBuilder::SliceBuilder::preSlice() +{ + arrow::Datum value_counts; + auto options = arrow::compute::ScalarAggregateOptions::Defaults(); + ARROW_ASSIGN_OR_RAISE(value_counts, arrow::compute::CallFunction("value_counts", {mSource}, &options)); + auto pair = static_cast(value_counts.array()); + mValues = std::make_shared>(pair.field(0)->data()); + mCounts = std::make_shared>(pair.field(1)->data()); + return arrow::Status::OK(); +} + +ArrayBuilder::ArrayBuilder(std::shared_ptr source, arrow::MemoryPool* pool) + : ChunkedArrayIterator{source} +{ + if (!preFind().ok()) { + throw framework::runtime_error("Cannot pre-find in a source for array-index building"); + } + + std::unique_ptr builder; + auto status = arrow::MakeBuilder(pool, arrow::int32(), &builder); + if (!status.ok()) { + throw framework::runtime_error("Cannot create array for the array-index builder!"); + } + mListBuilder = std::make_unique(pool, std::move(builder)); + mValueBuilder = static_cast(mListBuilder.get())->value_builder(); +} + +void ArrayBuilder::reset(std::shared_ptr source) +{ + static_cast(this)->reset(source); + if (!preFind().ok()) { + throw framework::runtime_error("Cannot pre-find in a source for array-index building"); + } + mValues.clear(); + mIndices.clear(); + mListBuilder->Reset(); +} + +bool ArrayBuilder::find(int idx) +{ + return (std::find(mValues.begin(), mValues.end(), idx) != mValues.end()); +} + +void ArrayBuilder::fill(int idx) { (void)static_cast(mListBuilder.get())->Append(); if (std::find(mValues.begin(), mValues.end(), idx) != mValues.end()) { @@ -226,6 +269,96 @@ void IndexColumnBuilder::fillMulti(int idx) } } +std::shared_ptr ArrayBuilder::result() const +{ + std::shared_ptr array; + auto status = static_cast(mListBuilder.get())->Finish(&array); + if (!status.ok()) { + cannotBuildAnArray(); + } + return std::make_shared(array); +} + +arrow::Status ArrayBuilder::preFind() +{ + arrow::Datum max; + auto options = arrow::compute::ScalarAggregateOptions::Defaults(); + ARROW_ASSIGN_OR_RAISE(max, arrow::compute::CallFunction("max", {mSource}, &options)); + auto maxValue = std::dynamic_pointer_cast(max.scalar())->value; + mIndices.resize(maxValue + 1); + + auto row = 0; + for (auto i = 0; i < mSource->length(); ++i) { + auto v = valueAt(i); + if (v >= 0) { + mValues.emplace_back(v); + mIndices[v].push_back(row); + } + ++row; + } + std::sort(mValues.begin(), mValues.end()); + + return arrow::Status::OK(); +} + +IndexColumnBuilder::IndexColumnBuilder(soa::IndexKind kind, int pos, arrow::MemoryPool* pool, std::shared_ptr source) + : mColumnPos{pos} +{ + switch (kind) { + case soa::IndexKind::IdxSelf: + builder = SelfBuilder{pool}; + break; + case soa::IndexKind::IdxSingle: + builder = SingleBuilder{source, pool}; + break; + case soa::IndexKind::IdxSlice: + builder = SliceBuilder{source, pool}; + break; + case soa::IndexKind::IdxArray: + builder = ArrayBuilder{source, pool}; + break; + default: + cannotCreateIndexBuilder(); + } +} + +void IndexColumnBuilder::reset(std::shared_ptr source) +{ + std::visit( + overloaded{ + [](std::monostate) {}, + [&source](auto& b) { b.reset(source); }}, + builder); +} + +bool IndexColumnBuilder::find(int idx) +{ + return std::visit( + overloaded{ + [](std::monostate) { return false; }, + [&idx](auto& b) { return b.find(idx); }, + }, + builder); +} + +void IndexColumnBuilder::fill(int idx) +{ + std::visit( + overloaded{ + [](std::monostate) {}, + [&idx](auto& b) { b.fill(idx); }}, + builder); +} + +std::shared_ptr IndexColumnBuilder::result() const +{ + return std::visit( + overloaded{ + [](std::monostate) -> std::shared_ptr { return nullptr; }, + [](auto& b) { return b.result(); }}, + builder); +} + std::shared_ptr ChunkedArrayIterator::getCurrentArray() { auto chunk = mSource->chunk(mChunk); @@ -265,14 +398,4 @@ int ChunkedArrayIterator::valueAt(size_t pos) } return *(mCurrent + pos); } - -std::shared_ptr makeArrowTable(const char* label, std::vector>&& columns, std::vector>&& fields) -{ - auto schema = std::make_shared(fields); - schema->WithMetadata( - std::make_shared( - std::vector{std::string{"label"}}, - std::vector{std::string{label}})); - return arrow::Table::Make(schema, columns); -} } // namespace o2::framework diff --git a/Framework/Core/src/IndexJSONHelpers.cxx b/Framework/Core/src/IndexJSONHelpers.cxx new file mode 100644 index 0000000000000..a5c6c70579599 --- /dev/null +++ b/Framework/Core/src/IndexJSONHelpers.cxx @@ -0,0 +1,240 @@ +// 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 "IndexJSONHelpers.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace o2::framework +{ +namespace +{ +struct IndexRecordsReader : public rapidjson::BaseReaderHandler, IndexRecordsReader> { + using Ch = rapidjson::UTF8<>::Ch; + using SizeType = rapidjson::SizeType; + + enum struct State { + IN_START, + IN_LIST, + IN_RECORD, + IN_ERROR + }; + + std::stack states; + std::ostringstream debug; + + std::vector records; + std::string currentKey; + std::string label; + std::string columnLabel; + std::string matcherStr; + o2::soa::IndexKind kind; + int pos; + + IndexRecordsReader() + { + debug << ">>> Start" << std::endl; + states.push(State::IN_START); + } + + bool StartArray() + { + debug << "StartArray()" << std::endl; + if (states.top() == State::IN_START && currentKey.compare("records") == 0) { + states.push(State::IN_LIST); + return true; + } + states.push(State::IN_ERROR); + return false; + } + + bool EndArray(SizeType) + { + debug << "EndArray()" << std::endl; + if (states.top() == State::IN_LIST) { + // records done + states.pop(); + return true; + } + states.push(State::IN_ERROR); + return false; + } + + bool Key(const Ch* str, SizeType, bool) + { + debug << "Key(" << str << ")" << std::endl; + currentKey = str; + if (states.top() == State::IN_START) { + if (currentKey.compare("records") == 0) { + return true; + } + } + + if (states.top() == State::IN_RECORD) { + if (currentKey.compare("label") == 0) { + return true; + } + if (currentKey.compare("matcher") == 0) { + return true; + } + if (currentKey.compare("column") == 0) { + return true; + } + if (currentKey.compare("kind") == 0) { + return true; + } + if (currentKey.compare("pos") == 0) { + return true; + } + } + + states.push(State::IN_ERROR); + return false; + } + + bool StartObject() + { + debug << "StartObject()" << std::endl; + if (states.top() == State::IN_START) { + return true; + } + + if (states.top() == State::IN_LIST) { + states.push(State::IN_RECORD); + label = ""; + kind = soa::IndexKind::IdxInvalid; + pos = -2; + return true; + } + + states.push(State::IN_ERROR); + return false; + } + + bool EndObject(SizeType) + { + debug << "EndObject()" << std::endl; + if (states.top() == State::IN_RECORD) { + states.pop(); + // add a record + records.emplace_back(label, DataSpecUtils::fromString(matcherStr), columnLabel, kind, pos); + return true; + } + + if (states.top() == State::IN_START) { + return true; + } + + states.push(State::IN_ERROR); + return false; + } + + bool Uint(unsigned i) + { + debug << "Uint(" << i << ") passed to Int()" << std::endl; + return Int(i); + } + + bool Int(int i) + { + debug << "Int(" << i << ")" << std::endl; + if (states.top() == State::IN_RECORD) { + if (currentKey.compare("kind") == 0) { + kind = (soa::IndexKind)i; + return true; + } + if (currentKey.compare("pos") == 0) { + pos = i; + return true; + } + } + + states.push(State::IN_ERROR); + return false; + } + + bool String(const Ch* str, SizeType, bool) + { + debug << "String(" << str << ")" << std::endl; + if (states.top() == State::IN_RECORD) { + if (currentKey.compare("label") == 0) { + label = str; + return true; + } + if (currentKey.compare("column") == 0) { + columnLabel = str; + return true; + } + if (currentKey.compare("matcher") == 0) { + matcherStr = str; + return true; + } + } + + states.push(State::IN_ERROR); + return false; + } +}; +} // namespace + +std::vector IndexJSONHelpers::read(std::istream& s) +{ + rapidjson::Reader reader; + rapidjson::IStreamWrapper isw(s); + IndexRecordsReader irreader; + + bool ok = reader.Parse(isw, irreader); + + if (!ok) { + throw framework::runtime_error_f("Cannot parse serialized index records vector, error: %s at offset: %d", rapidjson::GetParseError_En(reader.GetParseErrorCode()), reader.GetErrorOffset()); + } + return irreader.records; +} + +namespace +{ +void writeRecords(rapidjson::Writer& w, std::vector& records) +{ + for (auto& r : records) { + w.StartObject(); + w.Key("label"); + w.String(r.label.c_str()); + w.Key("matcher"); + w.String(DataSpecUtils::describe(r.matcher).c_str()); + w.Key("column"); + w.String(r.columnLabel.c_str()); + w.Key("kind"); + w.Int((int)r.kind); + w.Key("pos"); + w.Int(r.pos); + w.EndObject(); + } +} +} // namespace + +void IndexJSONHelpers::write(std::ostream& o, std::vector& irs) +{ + rapidjson::OStreamWrapper osw(o); + rapidjson::Writer w(osw); + w.StartObject(); + w.Key("records"); + w.StartArray(); + writeRecords(w, irs); + w.EndArray(); + w.EndObject(); +} +} // namespace o2::framework diff --git a/Framework/Core/src/IndexJSONHelpers.h b/Framework/Core/src/IndexJSONHelpers.h new file mode 100644 index 0000000000000..dee534ae390f5 --- /dev/null +++ b/Framework/Core/src/IndexJSONHelpers.h @@ -0,0 +1,25 @@ +// 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 INDEXJSONHELPERS_H +#define INDEXJSONHELPERS_H + +#include + +namespace o2::framework +{ +struct IndexJSONHelpers { + static std::vector read(std::istream& s); + static void write(std::ostream& o, std::vector& irs); +}; + +} // namespace o2::framework + +#endif // INDEXJSONHELPERS_H diff --git a/Framework/Core/src/InputRecord.cxx b/Framework/Core/src/InputRecord.cxx index c4c6852a6a3c7..18b341704ffcb 100644 --- a/Framework/Core/src/InputRecord.cxx +++ b/Framework/Core/src/InputRecord.cxx @@ -58,6 +58,11 @@ int InputRecord::getPos(const char* binding) const return -1; } +int InputRecord::getPos(ConcreteDataMatcher matcher) const +{ + return getPos(mInputsSchema, matcher).index; +} + InputRecord::InputPos InputRecord::getPos(std::vector const& schema, ConcreteDataMatcher concrete) { size_t inputIndex = 0; diff --git a/Framework/Core/src/PropertyTreeHelpers.cxx b/Framework/Core/src/PropertyTreeHelpers.cxx index 3f2356eb37824..18055edb635b4 100644 --- a/Framework/Core/src/PropertyTreeHelpers.cxx +++ b/Framework/Core/src/PropertyTreeHelpers.cxx @@ -14,12 +14,15 @@ #include "Framework/VariantPropertyTreeHelpers.h" #include "Framework/RuntimeError.h" #include "Framework/VariantJSONHelpers.h" +#include "Framework/Signpost.h" #include #include #include +O2_DECLARE_DYNAMIC_LOG(configuration); + namespace o2::framework { namespace @@ -37,6 +40,8 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s boost::property_tree::ptree& pt, boost::property_tree::ptree& provenance) { + O2_SIGNPOST_ID_GENERATE(cid, configuration); + O2_SIGNPOST_START(configuration, cid, "populateDefaults", "Filling with defaults"); for (auto const& spec : schema) { std::string key = spec.name.substr(0, spec.name.find(',')); try { @@ -77,9 +82,12 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s case VariantType::String: pt.put(key, spec.defaultValue.get()); break; - case VariantType::Bool: - pt.put(key, spec.defaultValue.get()); + case VariantType::Bool: { + bool value = spec.defaultValue.get(); + O2_SIGNPOST_EVENT_EMIT(configuration, cid, "populateDefaults", "Setting %{public}s: %{public}s", key.c_str(), value ? "true" : "false"); + pt.put(key, value); break; + } case VariantType::Dict: pt.put_child(key, boost::property_tree::ptree{}); break; @@ -126,13 +134,17 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s } provenance.put(key, "default"); } catch (std::runtime_error& re) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populateDefaults", "Aborting because of runtime_error %{public}s", re.what()); throw; } catch (std::exception& e) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populateDefaults", "Missing option %{public}s (%{public}s)", key.c_str(), e.what()); throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populateDefaults", "Aborting because of missing option %{public}s", key.c_str()); throw std::invalid_argument(std::string("missing option: ") + key); } } + O2_SIGNPOST_END(configuration, cid, "populateDefaults", "Done"); } void PropertyTreeHelpers::populate(std::vector const& schema, @@ -140,6 +152,8 @@ void PropertyTreeHelpers::populate(std::vector const& schema, boost::program_options::variables_map const& vmap, boost::property_tree::ptree& provenance) { + O2_SIGNPOST_ID_GENERATE(cid, configuration); + O2_SIGNPOST_START(configuration, cid, "populate", "Filling parameters from variables_map"); for (auto const& spec : schema) { // strip short version to get the correct key std::string key = spec.name.substr(0, spec.name.find(',')); @@ -183,9 +197,11 @@ void PropertyTreeHelpers::populate(std::vector const& schema, pt.put(key, *v); } break; - case VariantType::Bool: - pt.put(key, vmap[key].as()); - break; + case VariantType::Bool: { + auto v = vmap[key].as(); + O2_SIGNPOST_EVENT_EMIT(configuration, cid, "populate", "Setting %{public}s: %{public}s", key.c_str(), v ? "true" : "false"); + pt.put(key, v); + } break; case VariantType::ArrayInt: { auto v = fromString(vmap[key].as()); pt.put_child(key, vectorToBranch(v.get(), v.size())); @@ -243,13 +259,17 @@ void PropertyTreeHelpers::populate(std::vector const& schema, } provenance.put(key, "fairmq"); } catch (std::runtime_error& re) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting because of runtime_error %{public}s", re.what()); throw; } catch (std::exception& e) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Missing option %{public}s (%{public}s)", key.c_str(), e.what()); throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting because of missing option %{public}s", key.c_str()); throw std::invalid_argument(std::string("missing option: ") + key); } } + O2_SIGNPOST_END(configuration, cid, "populate", "Done"); } template @@ -273,6 +293,8 @@ void PropertyTreeHelpers::populate(std::vector const& schema, boost::property_tree::ptree& provenance, std::string const& provenanceLabel) { + O2_SIGNPOST_ID_GENERATE(cid, configuration); + O2_SIGNPOST_START(configuration, cid, "populate", "Filling parameters from ptree"); for (auto const& spec : schema) { // strip short version to get the correct key std::string key = spec.name.substr(0, spec.name.find(',')); @@ -318,9 +340,11 @@ void PropertyTreeHelpers::populate(std::vector const& schema, case VariantType::String: pt.put(key, (*it).get_value()); break; - case VariantType::Bool: + case VariantType::Bool: { + auto v = (*it).get_value(); + O2_SIGNPOST_EVENT_EMIT(configuration, cid, "populate", "Setting %{public}s: %{public}s", key.c_str(), v ? "true" : "false"); pt.put(key, (*it).get_value()); - break; + } break; case VariantType::Dict: case VariantType::ArrayInt: case VariantType::ArrayFloat: @@ -371,13 +395,18 @@ void PropertyTreeHelpers::populate(std::vector const& schema, } provenance.put(key, provenanceLabel); } catch (std::runtime_error& re) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting during processing of %{public}s because of runtime_error %{public}s", + key.c_str(), re.what()); throw; } catch (std::exception& e) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Missing option %{public}s (%{public}s)", key.c_str(), e.what()); throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting because of missing option %{public}s", key.c_str()); throw std::invalid_argument(std::string("missing option: ") + key); } } + O2_SIGNPOST_END(configuration, cid, "populate", "Done"); } namespace diff --git a/Framework/Core/src/ResourcePolicy.cxx b/Framework/Core/src/ResourcePolicy.cxx index 9076a87d547fa..18ff15e372657 100644 --- a/Framework/Core/src/ResourcePolicy.cxx +++ b/Framework/Core/src/ResourcePolicy.cxx @@ -18,8 +18,9 @@ namespace o2::framework std::vector ResourcePolicy::createDefaultPolicies() { + // FIXME: we should have better logic to decide if we can process something. return { - ResourcePolicyHelpers::sharedMemoryBoundTask("internal-dpl-aod-reader.*", 100000000), + ResourcePolicyHelpers::rateLimitedSharedMemoryBoundTask("internal-dpl-aod-reader.*", 100000000, 1), ResourcePolicyHelpers::trivialTask(".*")}; } diff --git a/Framework/Core/src/ResourcePolicyHelpers.cxx b/Framework/Core/src/ResourcePolicyHelpers.cxx index aad783cdc1f60..2c5c4f54dd9b5 100644 --- a/Framework/Core/src/ResourcePolicyHelpers.cxx +++ b/Framework/Core/src/ResourcePolicyHelpers.cxx @@ -11,9 +11,7 @@ #include "Framework/ResourcePolicyHelpers.h" #include "Framework/DeviceSpec.h" -#include "ResourcesMonitoringHelper.h" -#include #include namespace o2::framework @@ -41,6 +39,36 @@ ResourcePolicy ResourcePolicyHelpers::cpuBoundTask(char const* s, int requestedC [requestedCPUs](ComputingQuotaOffer const& offer, ComputingQuotaOffer const& accumulated) -> OfferScore { return accumulated.cpu >= requestedCPUs ? OfferScore::Enough : OfferScore::More; }}; } +ResourcePolicy ResourcePolicyHelpers::rateLimitedSharedMemoryBoundTask(char const* s, int requestedSharedMemory, int requestedTimeslices) +{ + return ResourcePolicy{ + "ratelimited-shm-bound", + [matcher = std::regex(s)](DeviceSpec const& spec) -> bool { + return std::regex_match(spec.name, matcher); + }, + [requestedSharedMemory, requestedTimeslices](ComputingQuotaOffer const& offer, ComputingQuotaOffer const& accumulated) -> OfferScore { + // If we have enough memory and not enough timeslices, + // ignore further shared memory. + if (accumulated.sharedMemory >= requestedSharedMemory && offer.timeslices == 0) { + return OfferScore::Unneeded; + } + // If we have enough timeslices and not enough shared memory + // ignore further timeslices. + if (accumulated.timeslices >= requestedTimeslices && offer.sharedMemory == 0) { + return OfferScore::Unneeded; + } + // If it does not offer neither shared memory nor timeslices, mark it as unneeded. + if (offer.sharedMemory == 0 && offer.timeslices == 0) { + return OfferScore::Unneeded; + } + // We have enough to process. + if ((accumulated.sharedMemory + offer.sharedMemory) >= requestedSharedMemory && (accumulated.timeslices + offer.timeslices) >= requestedTimeslices) { + return OfferScore::Enough; + } + // We need more resources + return OfferScore::More; }}; +} + ResourcePolicy ResourcePolicyHelpers::sharedMemoryBoundTask(char const* s, int requestedSharedMemory) { return ResourcePolicy{ @@ -52,7 +80,7 @@ ResourcePolicy ResourcePolicyHelpers::sharedMemoryBoundTask(char const* s, int r if (offer.sharedMemory == 0) { return OfferScore::Unneeded; } - return accumulated.sharedMemory >= requestedSharedMemory ? OfferScore::Enough : OfferScore::More; }}; + return (accumulated.sharedMemory + offer.sharedMemory)>= requestedSharedMemory ? OfferScore::Enough : OfferScore::More; }}; } } // namespace o2::framework diff --git a/Framework/Core/src/ResourcesMonitoringHelper.cxx b/Framework/Core/src/ResourcesMonitoringHelper.cxx index 1f69da80888c9..d457edb4844e5 100644 --- a/Framework/Core/src/ResourcesMonitoringHelper.cxx +++ b/Framework/Core/src/ResourcesMonitoringHelper.cxx @@ -8,12 +8,13 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. - #include "ResourcesMonitoringHelper.h" #include "Framework/DeviceMetricsInfo.h" -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#include +#include +#include + +#include +#include #include #include #include @@ -22,153 +23,147 @@ using namespace o2::framework; template -inline static T retriveValue(T val) -{ - return val; -} - -inline static std::string retriveValue(const std::reference_wrapper val) +void fillNodeWithValue(rapidjson::Writer& w, + size_t filledMetrics, + MetricsStorage const& metricsStorage, + TimestampsStorage const& timestampsStorage) { - return std::string(val.get().data); -} - -template -boost::property_tree::ptree fillNodeWithValue(const DeviceMetricsInfo& deviceMetrics, - const T& metricsStorage, const TIMESTAMPS& timestampsStorage, size_t labelIndex, size_t storeIndex) -{ - unsigned int loopRange = std::min(deviceMetrics.metrics[labelIndex].filledMetrics, metricsStorage[storeIndex].size()); - boost::property_tree::ptree metricNode; + unsigned int loopRange = std::min(filledMetrics, metricsStorage.size()); + w.StartArray(); for (unsigned int idx = 0; idx < loopRange; ++idx) { - boost::property_tree::ptree values; - values.add("timestamp", timestampsStorage[storeIndex][idx]); + w.StartObject(); + w.Key("timestamp"); + std::string s = std::to_string(timestampsStorage[idx]); + w.String(s.c_str(), s.size()); + w.Key("value"); if constexpr (std::is_arithmetic_v) { - values.add("value", std::to_string(retriveValue(std::cref(metricsStorage[storeIndex][idx])))); + w.String(std::to_string(metricsStorage[idx]).c_str()); } else { - values.add("value", retriveValue(std::cref(metricsStorage[storeIndex][idx]))); + w.String(metricsStorage[idx].data); } - metricNode.push_back(std::make_pair("", values)); + w.EndObject(); } - return metricNode; + w.EndArray(); } bool ResourcesMonitoringHelper::dumpMetricsToJSON(const std::vector& metrics, const DeviceMetricsInfo& driverMetrics, const std::vector& specs, - std::vector const& performanceMetricsRegex) noexcept + std::vector const& performanceMetricsRegex, + std::ostream& out) noexcept { - assert(metrics.size() == specs.size()); if (metrics.empty()) { return false; } - boost::property_tree::ptree root; - for (unsigned int idx = 0; idx < metrics.size(); ++idx) { + rapidjson::OStreamWrapper osw(out); + rapidjson::PrettyWriter w(osw); + // Top level obejct for all the metrics + w.StartObject(); + + for (unsigned int idx = 0; idx < metrics.size(); ++idx) { + w.Key(specs[idx].id.c_str()); const auto& deviceMetrics = metrics[idx]; - boost::property_tree::ptree deviceRoot; + w.StartObject(); for (size_t mi = 0; mi < deviceMetrics.metricLabels.size(); mi++) { std::string_view metricLabel{deviceMetrics.metricLabels[mi].label, deviceMetrics.metricLabels[mi].size}; auto same = [metricLabel](std::regex const& matcher) -> bool { return std::regex_match(metricLabel.begin(), metricLabel.end(), matcher); }; - //check if we are interested + // check if we are interested if (std::find_if(std::begin(performanceMetricsRegex), std::end(performanceMetricsRegex), same) == performanceMetricsRegex.end()) { continue; } auto storeIdx = deviceMetrics.metrics[mi].storeIdx; + size_t filledMetrics = deviceMetrics.metrics[mi].filledMetrics; if (deviceMetrics.metrics[mi].filledMetrics == 0) { continue; } - //if so - - boost::property_tree::ptree metricNode; - + w.Key(metricLabel.data(), metricLabel.size()); switch (deviceMetrics.metrics[mi].type) { case MetricType::Int: - metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.intMetrics, deviceMetrics.intTimestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, deviceMetrics.intMetrics[storeIdx], + deviceMetrics.intTimestamps[storeIdx]); break; case MetricType::Float: - metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.floatMetrics, deviceMetrics.floatTimestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, deviceMetrics.floatMetrics[storeIdx], + deviceMetrics.floatTimestamps[storeIdx]); break; case MetricType::String: - metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.stringMetrics, deviceMetrics.stringTimestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, deviceMetrics.stringMetrics[storeIdx], + deviceMetrics.stringTimestamps[storeIdx]); break; case MetricType::Uint64: - metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.uint64Metrics, deviceMetrics.uint64Timestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, deviceMetrics.uint64Metrics[storeIdx], + deviceMetrics.uint64Timestamps[storeIdx]); break; default: continue; } - deviceRoot.add_child(std::string(metricLabel), metricNode); } - root.add_child(specs[idx].id, deviceRoot); + w.EndObject(); } - boost::property_tree::ptree driverRoot; + w.Key("driver"); + w.StartObject(); for (size_t mi = 0; mi < driverMetrics.metricLabels.size(); mi++) { std::string_view const metricLabel{driverMetrics.metricLabels[mi].label, driverMetrics.metricLabels[mi].size}; auto same = [metricLabel](std::regex const& matcher) -> bool { return std::regex_match(metricLabel.begin(), metricLabel.end(), matcher); }; - //check if we are interested + // check if we are interested if (std::find_if(std::begin(performanceMetricsRegex), std::end(performanceMetricsRegex), same) == performanceMetricsRegex.end()) { continue; } auto storeIdx = driverMetrics.metrics[mi].storeIdx; // and if data is there - if (driverMetrics.metrics[mi].filledMetrics == 0) { + size_t filledMetrics = driverMetrics.metrics[mi].filledMetrics; + if (filledMetrics == 0) { continue; } - //if so - boost::property_tree::ptree metricNode; - + w.Key(metricLabel.data(), metricLabel.size()); switch (driverMetrics.metrics[mi].type) { case MetricType::Int: - metricNode = fillNodeWithValue(driverMetrics, driverMetrics.intMetrics, driverMetrics.intTimestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, driverMetrics.intMetrics[storeIdx], + driverMetrics.intTimestamps[storeIdx]); break; case MetricType::Float: - metricNode = fillNodeWithValue(driverMetrics, driverMetrics.floatMetrics, driverMetrics.floatTimestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, driverMetrics.floatMetrics[storeIdx], + driverMetrics.floatTimestamps[storeIdx]); break; case MetricType::String: - metricNode = fillNodeWithValue(driverMetrics, driverMetrics.stringMetrics, driverMetrics.stringTimestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, driverMetrics.stringMetrics[storeIdx], + driverMetrics.stringTimestamps[storeIdx]); break; case MetricType::Uint64: - metricNode = fillNodeWithValue(driverMetrics, driverMetrics.uint64Metrics, driverMetrics.uint64Timestamps, mi, storeIdx); + fillNodeWithValue(w, filledMetrics, driverMetrics.uint64Metrics[storeIdx], + driverMetrics.uint64Timestamps[storeIdx]); break; default: continue; } - driverRoot.add_child(std::string{metricLabel}, metricNode); } - - root.add_child("driver", driverRoot); - - std::ofstream file("performanceMetrics.json", std::ios::out); - if (file.is_open()) { - boost::property_tree::json_parser::write_json(file, root); - } else { - return false; - } - - file.close(); + w.EndObject(); + w.EndObject(); return true; } diff --git a/Framework/Core/src/ResourcesMonitoringHelper.h b/Framework/Core/src/ResourcesMonitoringHelper.h index fc725e201ea98..549f681231e35 100644 --- a/Framework/Core/src/ResourcesMonitoringHelper.h +++ b/Framework/Core/src/ResourcesMonitoringHelper.h @@ -13,27 +13,25 @@ #define O2_FRAMEWORK_RESOURCESMONITORINGHELPER_H_ #include "Framework/DeviceMetricsInfo.h" -#include "Monitoring/ProcessMonitor.h" #include "Framework/DeviceSpec.h" #include -#include #include +#include namespace o2::framework { - struct ResourcesMonitoringHelper { /// Dump the metrics in @a metrics which match the names specified in @a metricsToDump /// @a specs are the DeviceSpecs associated to the metrics. static bool dumpMetricsToJSON(std::vector const& metrics, DeviceMetricsInfo const& driverMetrics, std::vector const& specs, - std::vector const& metricsToDump) noexcept; + std::vector const& metricsToDump, + std::ostream& out) noexcept; static bool isResourcesMonitoringEnabled(unsigned short interval) noexcept { return interval > 0; } }; - } // namespace o2::framework #endif // O2_FRAMEWORK_RESOURCESMONITORINGHELPER_H_ diff --git a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h b/Framework/Core/src/ServiceRegistryRef.cxx similarity index 56% rename from Framework/TestWorkflows/src/o2_sim_its_ALP3.h rename to Framework/Core/src/ServiceRegistryRef.cxx index f9c465fcf5717..70728ad37eda7 100644 --- a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h +++ b/Framework/Core/src/ServiceRegistryRef.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -9,17 +9,17 @@ // 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 -#include "Framework/DataProcessorSpec.h" +#include "Framework/ServiceRegistryRef.h" +namespace o2::framework { -namespace o2 -{ -namespace workflows -{ -o2::framework::DataProcessorSpec sim_its_ALP3(); +ServiceRegistryRef *ServiceRegistryRef::globalDeviceRef(ServiceRegistryRef *ref) { + static ServiceRegistryRef *globalRef = nullptr; + if (!globalRef) { + globalRef = ref; + } + // We return a copy, so that it can be cache + return globalRef; } -} // namespace o2 -#endif // WORKFLOWS_O2_SIM_ITS_ALP3 +} diff --git a/Framework/Core/src/TMessageSerializer.cxx b/Framework/Core/src/TMessageSerializer.cxx index 81a1c6e537d09..bf9583f780957 100644 --- a/Framework/Core/src/TMessageSerializer.cxx +++ b/Framework/Core/src/TMessageSerializer.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. #include -#include +#include #include #include diff --git a/Framework/Core/src/TableBuilder.cxx b/Framework/Core/src/TableBuilder.cxx index 2169722efa9da..955fe686e12a8 100644 --- a/Framework/Core/src/TableBuilder.cxx +++ b/Framework/Core/src/TableBuilder.cxx @@ -81,53 +81,7 @@ void TableBuilder::validate() const void TableBuilder::setLabel(const char* label) { - mSchema = mSchema->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{label}})); -} - -std::shared_ptr spawnerHelper(std::shared_ptr const& fullTable, std::shared_ptr newSchema, size_t nColumns, - expressions::Projector* projectors, const char* name, - std::shared_ptr& projector) -{ - if (projector == nullptr) { - projector = framework::expressions::createProjectorHelper(nColumns, projectors, fullTable->schema(), newSchema->fields()); - } - - arrow::TableBatchReader reader(*fullTable); - std::shared_ptr batch; - arrow::ArrayVector v; - std::vector chunks; - chunks.resize(nColumns); - std::vector> arrays; - - while (true) { - auto s = reader.ReadNext(&batch); - if (!s.ok()) { - throw runtime_error_f("Cannot read batches from source table to spawn %s: %s", name, s.ToString().c_str()); - } - if (batch == nullptr) { - break; - } - try { - s = projector->Evaluate(*batch, arrow::default_memory_pool(), &v); - if (!s.ok()) { - throw runtime_error_f("Cannot apply projector to source table of %s: %s", name, s.ToString().c_str()); - } - } catch (std::exception& e) { - throw runtime_error_f("Cannot apply projector to source table of %s: exception caught: %s", name, e.what()); - } - - for (auto i = 0U; i < nColumns; ++i) { - chunks[i].emplace_back(v.at(i)); - } - } - - arrays.reserve(nColumns); - for (auto i = 0U; i < nColumns; ++i) { - arrays.push_back(std::make_shared(chunks[i])); - } - - addLabelToSchema(newSchema, name); - return arrow::Table::Make(newSchema, arrays); + addLabelToSchema(mSchema, label); } } // namespace o2::framework diff --git a/Framework/Core/src/Variant.cxx b/Framework/Core/src/Variant.cxx index 21eb6103aa56a..e54a973bd4413 100644 --- a/Framework/Core/src/Variant.cxx +++ b/Framework/Core/src/Variant.cxx @@ -89,29 +89,29 @@ Variant::Variant(const Variant& other) : mType(other.mType) // In case this is an array we need to duplicate it to avoid // double deletion. switch (mType) { - case variant_trait_v: + case VariantType::String: mSize = other.mSize; variant_helper::set(&mStore, other.get()); return; - case variant_trait_v: + case VariantType::ArrayInt: mSize = other.mSize; variant_helper::set(&mStore, other.get(), mSize); return; - case variant_trait_v: + case VariantType::ArrayFloat: mSize = other.mSize; variant_helper::set(&mStore, other.get(), mSize); return; - case variant_trait_v: + case VariantType::ArrayDouble: mSize = other.mSize; variant_helper::set(&mStore, other.get(), mSize); return; - case variant_trait_v: + case VariantType::ArrayBool: mSize = other.mSize; variant_helper::set(&mStore, other.get(), mSize); return; - case variant_trait_v: + case VariantType::ArrayString: mSize = other.mSize; - variant_helper::set(&mStore, other.get(), mSize); + variant_helper>::set(&mStore, other.get>()); return; default: mStore = other.mStore; @@ -124,23 +124,14 @@ Variant::Variant(Variant&& other) noexcept : mType(other.mType) mStore = other.mStore; mSize = other.mSize; switch (mType) { - case variant_trait_v: - *reinterpret_cast(&(other.mStore)) = nullptr; - return; - case variant_trait_v: - *reinterpret_cast(&(other.mStore)) = nullptr; - return; - case variant_trait_v: - *reinterpret_cast(&(other.mStore)) = nullptr; - return; - case variant_trait_v: - *reinterpret_cast(&(other.mStore)) = nullptr; - return; - case variant_trait_v: - *reinterpret_cast(&(other.mStore)) = nullptr; + case VariantType::String: + case VariantType::ArrayInt: + case VariantType::ArrayFloat: + case VariantType::ArrayDouble: + case VariantType::ArrayBool: + case VariantType::ArrayString: + *reinterpret_cast(&(other.mStore)) = nullptr; return; - case variant_trait_v: - *reinterpret_cast(&(other.mStore)) = nullptr; default: return; } @@ -151,16 +142,20 @@ Variant::~Variant() // In case we allocated an array, we // should delete it. switch (mType) { - case variant_trait_v: - case variant_trait_v: - case variant_trait_v: - case variant_trait_v: - case variant_trait_v: - case variant_trait_v: + case VariantType::String: + case VariantType::ArrayInt: + case VariantType::ArrayFloat: + case VariantType::ArrayDouble: + case VariantType::ArrayBool: { if (reinterpret_cast(&mStore) != nullptr) { free(*reinterpret_cast(&mStore)); } return; + } + case VariantType::ArrayString: { + // Allocated with placement new. Nothing to delete. + return; + } default: return; } @@ -171,23 +166,23 @@ Variant& Variant::operator=(const Variant& other) mSize = other.mSize; mType = other.mType; switch (mType) { - case variant_trait_v: + case VariantType::String: variant_helper::set(&mStore, other.get()); return *this; - case variant_trait_v: + case VariantType::ArrayInt: variant_helper::set(&mStore, other.get(), mSize); return *this; - case variant_trait_v: + case VariantType::ArrayFloat: variant_helper::set(&mStore, other.get(), mSize); return *this; - case variant_trait_v: + case VariantType::ArrayDouble: variant_helper::set(&mStore, other.get(), mSize); return *this; - case variant_trait_v: + case VariantType::ArrayBool: variant_helper::set(&mStore, other.get(), mSize); return *this; - case variant_trait_v: - variant_helper::set(&mStore, other.get(), mSize); + case VariantType::ArrayString: + variant_helper>::set(&mStore, other.get>()); return *this; default: mStore = other.mStore; @@ -200,29 +195,29 @@ Variant& Variant::operator=(Variant&& other) noexcept mSize = other.mSize; mType = other.mType; switch (mType) { - case variant_trait_v: + case VariantType::String: variant_helper::set(&mStore, other.get()); *reinterpret_cast(&(other.mStore)) = nullptr; return *this; - case variant_trait_v: + case VariantType::ArrayInt: variant_helper::set(&mStore, other.get(), mSize); *reinterpret_cast(&(other.mStore)) = nullptr; return *this; - case variant_trait_v: + case VariantType::ArrayFloat: variant_helper::set(&mStore, other.get(), mSize); *reinterpret_cast(&(other.mStore)) = nullptr; return *this; - case variant_trait_v: + case VariantType::ArrayDouble: variant_helper::set(&mStore, other.get(), mSize); *reinterpret_cast(&(other.mStore)) = nullptr; return *this; - case variant_trait_v: + case VariantType::ArrayBool: variant_helper::set(&mStore, other.get(), mSize); *reinterpret_cast(&(other.mStore)) = nullptr; return *this; - case variant_trait_v: - variant_helper::set(&mStore, other.get(), mSize); - *reinterpret_cast(&(other.mStore)) = nullptr; + case VariantType::ArrayString: + variant_helper>::set(&mStore, other.get>()); + *reinterpret_cast**>(&(other.mStore)) = nullptr; return *this; default: mStore = other.mStore; diff --git a/Framework/Core/src/VariantPropertyTreeHelpers.cxx b/Framework/Core/src/VariantPropertyTreeHelpers.cxx index 2b1746aae2c66..cb0aefaab39ec 100644 --- a/Framework/Core/src/VariantPropertyTreeHelpers.cxx +++ b/Framework/Core/src/VariantPropertyTreeHelpers.cxx @@ -19,17 +19,17 @@ template boost::property_tree::ptree o2::framework::basicVectorToBranch(float*, template boost::property_tree::ptree o2::framework::basicVectorToBranch(int*, size_t); template boost::property_tree::ptree o2::framework::basicVectorToBranch(double*, size_t); template boost::property_tree::ptree o2::framework::basicVectorToBranch(bool*, size_t); -template boost::property_tree::ptree o2::framework::basicVectorToBranch(std::basic_string*, size_t); +template boost::property_tree::ptree o2::framework::basicVectorToBranch(std::basic_string const*, size_t); template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); template boost::property_tree::ptree o2::framework::vectorToBranch(std::vector&& values); -template boost::property_tree::ptree o2::framework::vectorToBranch(float*, size_t); -template boost::property_tree::ptree o2::framework::vectorToBranch(int*, size_t); -template boost::property_tree::ptree o2::framework::vectorToBranch(double*, size_t); -template boost::property_tree::ptree o2::framework::vectorToBranch(bool*, size_t); -template boost::property_tree::ptree o2::framework::vectorToBranch(std::basic_string*, size_t); +template boost::property_tree::ptree o2::framework::vectorToBranch(float const*, size_t); +template boost::property_tree::ptree o2::framework::vectorToBranch(int const*, size_t); +template boost::property_tree::ptree o2::framework::vectorToBranch(double const*, size_t); +template boost::property_tree::ptree o2::framework::vectorToBranch(bool const*, size_t); +template boost::property_tree::ptree o2::framework::vectorToBranch(std::basic_string const*, size_t); template boost::property_tree::ptree o2::framework::labeledArrayToBranch(o2::framework::LabeledArray&& array); template boost::property_tree::ptree o2::framework::labeledArrayToBranch(o2::framework::LabeledArray&& array); diff --git a/Framework/Core/src/WSDriverClient.cxx b/Framework/Core/src/WSDriverClient.cxx index 179b13bf91d76..43a407536cb59 100644 --- a/Framework/Core/src/WSDriverClient.cxx +++ b/Framework/Core/src/WSDriverClient.cxx @@ -26,6 +26,7 @@ O2_DECLARE_DYNAMIC_LOG(completion); O2_DECLARE_DYNAMIC_LOG(monitoring_service); O2_DECLARE_DYNAMIC_LOG(data_processor_context); O2_DECLARE_DYNAMIC_LOG(stream_context); +O2_DECLARE_DYNAMIC_LOG(ws_client); namespace o2::framework { @@ -49,8 +50,8 @@ struct ClientWebSocketHandler : public WebSocketHandler { mClient.dispatch(std::string_view(frame, s)); } - void endFragmentation() override{}; - void control(char const* frame, size_t s) override{}; + void endFragmentation() override {}; + void control(char const* frame, size_t s) override {}; /// Invoked at the beginning of some incoming data. We simply /// reset actions which need to happen on a per chunk basis. @@ -119,6 +120,34 @@ void on_connect(uv_connect_t* connection, int status) state.pendingOffers.push_back(offer); }); + client->observe("/timeslice-offer", [ref = context->ref](std::string_view cmd) { + O2_SIGNPOST_ID_GENERATE(wid, ws_client); + O2_SIGNPOST_START(ws_client, wid, "timeslice-offer", "Received timeslice offer."); + auto& state = ref.get(); + static constexpr int prefixSize = std::string_view{"/timeslice-offer "}.size(); + if (prefixSize > cmd.size()) { + O2_SIGNPOST_END_WITH_ERROR(ws_client, wid, "timeslice-offer", "Malformed timeslice offer"); + return; + } + cmd.remove_prefix(prefixSize); + int64_t offerSize; + auto offerSizeError = std::from_chars(cmd.data(), cmd.data() + cmd.size(), offerSize); + if (offerSizeError.ec != std::errc()) { + O2_SIGNPOST_END_WITH_ERROR(ws_client, wid, "timeslice-offer", "Unexpected timeslice offer size"); + return; + } + ComputingQuotaOffer offer{ + .cpu = 0, + .memory = 0, + .sharedMemory = 0, + .timeslices = offerSize, + .runtime = 10000, + .user = -1, + .valid = true}; + state.pendingOffers.push_back(offer); + O2_SIGNPOST_END(ws_client, wid, "timeslice-offer", "Received %lli timeslices offer. Total pending offers %zu.", + offerSize, state.pendingOffers.size()); + }); client->observe("/quit", [ref = context->ref](std::string_view) { auto& state = ref.get(); diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 36583035c41ff..714706952d26c 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -11,7 +11,6 @@ #include "WorkflowHelpers.h" #include "Framework/AnalysisSupportHelpers.h" #include "Framework/AlgorithmSpec.h" -#include "Framework/AODReaderHelpers.h" #include "Framework/ConfigParamSpec.h" #include "Framework/ConfigParamsHelper.h" #include "Framework/CommonDataProcessors.h" @@ -20,7 +19,6 @@ #include "Framework/DataSpecUtils.h" #include "Framework/DataSpecViews.h" #include "Framework/DataAllocator.h" -#include "Framework/ControlService.h" #include "Framework/RawDeviceService.h" #include "Framework/StringHelpers.h" #include "Framework/ChannelSpecHelpers.h" @@ -38,6 +36,7 @@ #include #include #include +#include O2_DECLARE_DYNAMIC_LOG(workflow_helpers); @@ -101,7 +100,7 @@ std::vector // which have the current node as incoming. // nextEdges will contain all the edges which are not related // to the current node. - for (auto& ei : remainingEdgesIndex) { + for (auto const& ei : remainingEdgesIndex) { if (*(edgeIn + ei * stride) == node.index) { nextVertex.insert({*(edgeOut + ei * stride), node.layer + 1}); } else { @@ -113,7 +112,7 @@ std::vector // Of all the vertices which have node as incoming, // check if there is any other incoming node. std::set hasPredecessors; - for (auto& ei : remainingEdgesIndex) { + for (auto const& ei : remainingEdgesIndex) { for (auto& m : nextVertex) { if (m.index == *(edgeOut + ei * stride)) { hasPredecessors.insert({m.index, m.layer}); @@ -157,18 +156,7 @@ int defaultConditionQueryRateMultiplier() void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext& ctx) { - auto fakeCallback = AlgorithmSpec{[](InitContext& ic) { - LOG(info) << "This is not a real device, merely a placeholder for external inputs"; - LOG(info) << "To be hidden / removed at some point."; - // mark this dummy process as ready-to-quit - ic.services().get().readyToQuit(QuitRequest::Me); - - return [](ProcessingContext& pc) { - // this callback is never called since there is no expiring input - pc.services().get().waitFor(2000); - }; - }}; - + int rateLimitingIPCID = std::stoi(ctx.options().get("timeframes-rate-limit-ipcid")); DataProcessorSpec ccdbBackend{ .name = "internal-dpl-ccdb-backend", .outputs = {}, @@ -243,25 +231,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext ConfigParamSpec{"step-value-enumeration", VariantType::Int64, 1ll, {"step between one value and the other"}}}, .requiredServices = CommonServices::defaultServices("O2FrameworkAnalysisSupport:RunSummary")}; - // AOD reader can be rate limited - int rateLimitingIPCID = std::stoi(ctx.options().get("timeframes-rate-limit-ipcid")); - std::string rateLimitingChannelConfigInput; - std::string rateLimitingChannelConfigOutput; - bool internalRateLimiting = false; - - // In case we have rate-limiting requested, any device without an input will get one on the special - // "DPL/RATE" message. - if (rateLimitingIPCID >= 0) { - rateLimitingChannelConfigInput = fmt::format("name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", - ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); - rateLimitingChannelConfigOutput = fmt::format("name=metric-feedback,type=push,method=bind,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", - ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); - internalRateLimiting = true; - aodReader.options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, rateLimitingChannelConfigInput, {"how many timeframes can be in flight at the same time"}}); - } - - ctx.services().registerService(ServiceRegistryHelpers::handleForService(new AnalysisContext)); - auto& ac = ctx.services().get(); + ctx.services().registerService(ServiceRegistryHelpers::handleForService(new DanglingEdgesContext)); + auto& dec = ctx.services().get(); std::vector requestedCCDBs; std::vector providedCCDBs; @@ -269,8 +240,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext for (size_t wi = 0; wi < workflow.size(); ++wi) { auto& processor = workflow[wi]; auto name = processor.name; - auto hash = runtime_hash(name.c_str()); - ac.outTskMap.push_back({hash, name}); + uint32_t hash = runtime_hash(name.c_str()); + dec.outTskMap.push_back({hash, name}); std::string prefix = "internal-dpl-"; if (processor.inputs.empty() && processor.name.compare(0, prefix.size(), prefix) != 0) { @@ -281,37 +252,24 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext processor.options.push_back(ConfigParamSpec{"end-value-enumeration", VariantType::Int64, -1ll, {"final value for the enumeration"}}); processor.options.push_back(ConfigParamSpec{"step-value-enumeration", VariantType::Int64, 1ll, {"step between one value and the other"}}); } - bool hasTimeframeInputs = false; - for (auto& input : processor.inputs) { - if (input.lifetime == Lifetime::Timeframe) { - hasTimeframeInputs = true; - break; - } - } - bool hasTimeframeOutputs = false; - for (auto& output : processor.outputs) { - if (output.lifetime == Lifetime::Timeframe) { - hasTimeframeOutputs = true; - break; - } - } + bool hasTimeframeInputs = std::ranges::any_of(processor.inputs, [](auto const& input) { return input.lifetime == Lifetime::Timeframe; }); + bool hasTimeframeOutputs = std::ranges::any_of(processor.outputs, [](auto const& output) { return output.lifetime == Lifetime::Timeframe; }); + // A timeframeSink consumes timeframes without creating new // timeframe data. bool timeframeSink = hasTimeframeInputs && !hasTimeframeOutputs; - if (std::stoi(ctx.options().get("timeframes-rate-limit-ipcid")) != -1) { + if (rateLimitingIPCID != -1) { if (timeframeSink && processor.name.find("internal-dpl-injected-dummy-sink") == std::string::npos) { O2_SIGNPOST_ID_GENERATE(sid, workflow_helpers); - uint32_t hash = runtime_hash(processor.name.c_str()); bool hasMatch = false; ConcreteDataMatcher summaryMatcher = ConcreteDataMatcher{"DPL", "SUMMARY", static_cast(hash)}; - for (auto& output : processor.outputs) { - if (DataSpecUtils::match(output, summaryMatcher)) { - O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "output enumeration", "%{public}s already there in %{public}s", - DataSpecUtils::describe(output).c_str(), processor.name.c_str()); - hasMatch = true; - break; - } + auto summaryOutput = std::ranges::find_if(processor.outputs, [&summaryMatcher](auto const& output) { return DataSpecUtils::match(output, summaryMatcher); }); + if (summaryOutput != processor.outputs.end()) { + O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "output enumeration", "%{public}s already there in %{public}s", + DataSpecUtils::describe(*summaryOutput).c_str(), processor.name.c_str()); + hasMatch = true; } + if (!hasMatch) { O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "output enumeration", "Adding DPL/SUMMARY/%d to %{public}s", hash, processor.name.c_str()); processor.outputs.push_back(OutputSpec{{"dpl-summary"}, ConcreteDataMatcher{"DPL", "SUMMARY", static_cast(hash)}}); @@ -324,7 +282,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext switch (input.lifetime) { case Lifetime::Timer: { auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - auto hasOption = std::any_of(processor.options.begin(), processor.options.end(), [&input](auto const& option) { return (option.name == "period-" + input.binding); }); + auto hasOption = std::ranges::any_of(processor.options, [&input](auto const& option) { return (option.name == "period-" + input.binding); }); if (hasOption == false) { processor.options.push_back(ConfigParamSpec{"period-" + input.binding, VariantType::Int, 1000, {"period of the timer in milliseconds"}}); } @@ -339,22 +297,16 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext timer.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec, Lifetime::Enumeration}); } break; case Lifetime::Condition: { - for (auto& option : processor.options) { - if (option.name == "condition-backend") { - hasConditionOption = true; - break; - } - } - if (hasConditionOption == false) { + requestedCCDBs.emplace_back(input); + if ((hasConditionOption == false) && std::ranges::none_of(processor.options, [](auto const& option) { return (option.name.compare("condition-backend") == 0); })) { processor.options.emplace_back(ConfigParamSpec{"condition-backend", VariantType::String, defaultConditionBackend(), {"URL for CCDB"}}); processor.options.emplace_back(ConfigParamSpec{"condition-timestamp", VariantType::Int64, 0ll, {"Force timestamp for CCDB lookup"}}); hasConditionOption = true; } - requestedCCDBs.emplace_back(input); } break; case Lifetime::OutOfBand: { auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - auto hasOption = std::any_of(processor.options.begin(), processor.options.end(), [&input](auto const& option) { return (option.name == "out-of-band-channel-name-" + input.binding); }); + auto hasOption = std::ranges::any_of(processor.options, [&input](auto const& option) { return (option.name == "out-of-band-channel-name-" + input.binding); }); if (hasOption == false) { processor.options.push_back(ConfigParamSpec{"out-of-band-channel-name-" + input.binding, VariantType::String, "out-of-band", {"channel to listen for out of band data"}}); } @@ -367,33 +319,33 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext break; } if (DataSpecUtils::partialMatch(input, AODOrigins)) { - DataSpecUtils::updateInputList(ac.requestedAODs, InputSpec{input}); + DataSpecUtils::updateInputList(dec.requestedAODs, InputSpec{input}); } if (DataSpecUtils::partialMatch(input, header::DataOrigin{"DYN"})) { - DataSpecUtils::updateInputList(ac.requestedDYNs, InputSpec{input}); + DataSpecUtils::updateInputList(dec.requestedDYNs, InputSpec{input}); } if (DataSpecUtils::partialMatch(input, header::DataOrigin{"IDX"})) { - DataSpecUtils::updateInputList(ac.requestedIDXs, InputSpec{input}); + DataSpecUtils::updateInputList(dec.requestedIDXs, InputSpec{input}); } if (DataSpecUtils::partialMatch(input, header::DataOrigin{"ATIM"})) { - DataSpecUtils::updateInputList(ac.requestedTIMs, InputSpec{input}); + DataSpecUtils::updateInputList(dec.requestedTIMs, InputSpec{input}); } } - std::stable_sort(timer.outputs.begin(), timer.outputs.end(), [](OutputSpec const& a, OutputSpec const& b) { return *DataSpecUtils::getOptionalSubSpec(a) < *DataSpecUtils::getOptionalSubSpec(b); }); + std::ranges::stable_sort(timer.outputs, [](OutputSpec const& a, OutputSpec const& b) { return *DataSpecUtils::getOptionalSubSpec(a) < *DataSpecUtils::getOptionalSubSpec(b); }); for (auto& output : processor.outputs) { if (DataSpecUtils::partialMatch(output, AODOrigins)) { - ac.providedAODs.emplace_back(output); + dec.providedAODs.emplace_back(output); } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"DYN"})) { - ac.providedDYNs.emplace_back(output); + dec.providedDYNs.emplace_back(output); } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"ATIM"})) { - ac.providedTIMs.emplace_back(output); + dec.providedTIMs.emplace_back(output); } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"ATSK"})) { - ac.providedOutputObjHist.emplace_back(output); - auto it = std::find_if(ac.outObjHistMap.begin(), ac.outObjHistMap.end(), [&](auto&& x) { return x.id == hash; }); - if (it == ac.outObjHistMap.end()) { - ac.outObjHistMap.push_back({hash, {output.binding.value}}); + dec.providedOutputObjHist.emplace_back(output); + auto it = std::ranges::find_if(dec.outObjHistMap, [&](auto&& x) { return x.id == hash; }); + if (it == dec.outObjHistMap.end()) { + dec.outObjHistMap.push_back({hash, {output.binding.value}}); } else { it->bindings.push_back(output.binding.value); } @@ -406,40 +358,38 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext auto inputSpecLessThan = [](InputSpec const& lhs, InputSpec const& rhs) { return DataSpecUtils::describe(lhs) < DataSpecUtils::describe(rhs); }; auto outputSpecLessThan = [](OutputSpec const& lhs, OutputSpec const& rhs) { return DataSpecUtils::describe(lhs) < DataSpecUtils::describe(rhs); }; - std::sort(ac.requestedDYNs.begin(), ac.requestedDYNs.end(), inputSpecLessThan); - std::sort(ac.requestedTIMs.begin(), ac.requestedTIMs.end(), inputSpecLessThan); - std::sort(ac.providedDYNs.begin(), ac.providedDYNs.end(), outputSpecLessThan); - std::sort(ac.providedTIMs.begin(), ac.providedTIMs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedDYNs, inputSpecLessThan); + std::ranges::sort(dec.requestedTIMs, inputSpecLessThan); + std::ranges::sort(dec.providedDYNs, outputSpecLessThan); + std::ranges::sort(dec.providedTIMs, outputSpecLessThan); DataProcessorSpec indexBuilder{ "internal-dpl-aod-index-builder", {}, {}, - readers::AODReaderHelpers::indexBuilderCallback(ac.requestedIDXs), + AlgorithmSpec::dummyAlgorithm(), // real algorithm will be set in adjustTopology {}}; - AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.requestedIDXs, ac.requestedAODs, ac.requestedDYNs, indexBuilder); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.requestedIDXs, dec.requestedAODs, dec.requestedDYNs, indexBuilder); - ac.requestedTIMs | views::filter_not_matching(ac.providedTIMs) | sinks::append_to{ac.analysisCCDBInputs}; + dec.requestedTIMs | views::filter_not_matching(dec.providedTIMs) | sinks::append_to{dec.analysisCCDBInputs}; DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); if (deploymentMode != DeploymentMode::OnlineDDS && deploymentMode != DeploymentMode::OnlineECS) { - AnalysisSupportHelpers::addMissingOutputsToAnalysisCCDBFetcher({}, ac.analysisCCDBInputs, ac.requestedAODs, ac.requestedTIMs, analysisCCDBBackend); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.analysisCCDBInputs, dec.requestedAODs, dec.requestedTIMs, analysisCCDBBackend); } - for (auto& input : ac.requestedDYNs) { - if (std::none_of(ac.providedDYNs.begin(), ac.providedDYNs.end(), [&input](auto const& x) { return DataSpecUtils::match(input, x); })) { - ac.spawnerInputs.emplace_back(input); - } - } + dec.requestedDYNs | views::filter_not_matching(dec.providedDYNs) | sinks::append_to{dec.spawnerInputs}; DataProcessorSpec aodSpawner{ "internal-dpl-aod-spawner", {}, {}, - readers::AODReaderHelpers::aodSpawnerCallback(ac.spawnerInputs), + AlgorithmSpec::dummyAlgorithm(), // real algorithm will be set in adjustTopology {}}; - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, aodSpawner); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, aodSpawner); + AnalysisSupportHelpers::addMissingOutputsToReader(dec.providedAODs, dec.requestedAODs, aodReader); - AnalysisSupportHelpers::addMissingOutputsToReader(ac.providedAODs, ac.requestedAODs, aodReader); + std::ranges::sort(requestedCCDBs, inputSpecLessThan); + std::ranges::sort(providedCCDBs, outputSpecLessThan); AnalysisSupportHelpers::addMissingOutputsToReader(providedCCDBs, requestedCCDBs, ccdbBackend); std::vector extraSpecs; @@ -461,25 +411,19 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto mctracks2aod = std::find_if(workflow.begin(), workflow.end(), [](auto const& x) { return x.name == "mctracks-to-aod"; }); + auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); if (mctracks2aod == workflow.end()) { // add normal reader - auto&& algo = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx); - if (internalRateLimiting) { - aodReader.algorithm = CommonDataProcessors::wrapWithRateLimiting(algo); - } else { - aodReader.algorithm = algo; - } aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); aodReader.outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); } else { - // AODs are being injected on-the-fly, add dummy reader + // AODs are being injected on-the-fly, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( - [outputs = aodReader.outputs](DeviceSpec const&) { + [](DeviceSpec const& spec) { LOGP(warn, "Workflow with injected AODs has unsatisfied inputs:"); - for (auto const& output : outputs) { - LOGP(warn, " {}", DataSpecUtils::describe(output)); + for (auto const& output : spec.outputs) { + LOGP(warn, " {}", DataSpecUtils::describe(output.matcher)); } LOGP(fatal, "Stopping."); // to ensure the output type for adaptStateful @@ -491,120 +435,84 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext extraSpecs.push_back(timePipeline(aodReader, ctx.options().get("readers"))); } - ConcreteDataMatcher dstf{"FLP", "DISTSUBTIMEFRAME", 0xccdb}; - if (ccdbBackend.outputs.empty() == false) { - ccdbBackend.outputs.push_back(OutputSpec{"CTP", "OrbitReset", 0}); - InputSpec matcher{"dstf", "FLP", "DISTSUBTIMEFRAME", 0xccdb}; - bool providesDISTSTF = false; - // Check if any of the provided outputs is a DISTSTF - // Check if any of the requested inputs is for a 0xccdb message - for (auto& dp : workflow) { - for (auto& output : dp.outputs) { - if (DataSpecUtils::match(matcher, output)) { - providesDISTSTF = true; - dstf = DataSpecUtils::asConcreteDataMatcher(output); - break; - } - } - if (providesDISTSTF) { - break; - } + InputSpec matcher{"dstf", "FLP", "DISTSUBTIMEFRAME", 0xccdb}; + auto& dstf = std::get(matcher.matcher); + // Check if any of the provided outputs is a DISTSTF + // Check if any of the requested inputs is for a 0xccdb message + bool providesDISTSTF = std::ranges::any_of(workflow, + [&matcher](auto const& dp) { + return std::any_of(dp.outputs.begin(), dp.outputs.end(), [&matcher](auto const& output) { + return DataSpecUtils::match(matcher, output); + }); + }); + + // If there is no CCDB requested, but we still ask for a FLP/DISTSUBTIMEFRAME/0xccdb + // we add to the first data processor which has no inputs (apart from + // enumerations / timers) the responsibility to provide the DISTSUBTIMEFRAME + bool requiresDISTSUBTIMEFRAME = std::ranges::any_of(workflow, + [&dstf](auto const& dp) { + return std::any_of(dp.inputs.begin(), dp.inputs.end(), [&dstf](auto const& input) { + return DataSpecUtils::match(input, dstf); + }); + }); + + // We find the first device which has either just enumerations or + // just timers, and we will add the DISTSUBTIMEFRAME to it. + // Notice how we do so in a stable manner by sorting the devices + // by name. + int enumCandidate = -1; + int timerCandidate = -1; + for (auto wi = 0U; wi < workflow.size(); ++wi) { + auto& dp = workflow[wi]; + if (dp.inputs.size() != 1) { + continue; + } + auto lifetime = dp.inputs[0].lifetime; + if (lifetime == Lifetime::Enumeration && (enumCandidate == -1 || workflow[enumCandidate].name > dp.name)) { + enumCandidate = wi; + } + if (lifetime == Lifetime::Timer && (timerCandidate == -1 || workflow[timerCandidate].name > dp.name)) { + timerCandidate = wi; } - // * If there are AOD outputs we use TFNumber as the CCDB clock - // * If one device provides a DISTSTF we use that as the CCDB clock - // * If one of the devices provides a timer we use that as the CCDB clock - // * If none of the above apply add to the first data processor - // which has no inputs apart from enumerations the responsibility - // to provide the DISTSUBTIMEFRAME. + } + + // * If there are AOD outputs we use TFNumber as the CCDB clock + // * If one device provides a DISTSTF we use that as the CCDB clock + // * If one of the devices provides a timer we use that as the CCDB clock + // * If none of the above apply, add to the first data processor + // which has no inputs apart from enumerations the responsibility + // to provide the DISTSUBTIMEFRAME. + if (ccdbBackend.outputs.empty() == false) { if (aodReader.outputs.empty() == false) { + // fetcher clock follows AOD source (TFNumber) ccdbBackend.inputs.push_back(InputSpec{"tfn", "TFN", "TFNumber"}); } else if (providesDISTSTF) { + // fetcher clock follows DSTF/ccdb source (DISTSUBTIMEFRAME) ccdbBackend.inputs.push_back(InputSpec{"tfn", dstf, Lifetime::Timeframe}); } else { - // We find the first device which has either just enumerations or - // just timers, and we add the DISTSUBTIMEFRAME to it. - // Notice how we do so in a stable manner by sorting the devices - // by name. - int enumCandidate = -1; - int timerCandidate = -1; - for (size_t wi = 0; wi < workflow.size(); wi++) { - auto& dp = workflow[wi]; - if (dp.inputs.size() != 1) { - continue; - } - auto lifetime = dp.inputs[0].lifetime; - if (lifetime == Lifetime::Enumeration && (enumCandidate == -1 || workflow[enumCandidate].name > dp.name)) { - enumCandidate = wi; - } - if (lifetime == Lifetime::Timer && (timerCandidate == -1 || workflow[timerCandidate].name > dp.name)) { - timerCandidate = wi; - } - } if (enumCandidate != -1) { - auto& dp = workflow[enumCandidate]; - DataSpecUtils::updateOutputList(dp.outputs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); + // add DSTF/ccdb source to the enumeration-driven source explicitly + // fetcher clock is provided by enumeration-driven source (DISTSUBTIMEFRAME) + DataSpecUtils::updateOutputList(workflow[enumCandidate].outputs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); ccdbBackend.inputs.push_back(InputSpec{"tfn", dstf, Lifetime::Timeframe}); } else if (timerCandidate != -1) { - auto& dp = workflow[timerCandidate]; - dstf = DataSpecUtils::asConcreteDataMatcher(dp.outputs[0]); - ccdbBackend.inputs.push_back(InputSpec{{"tfn"}, dstf, Lifetime::Timeframe}); + // fetcher clock is proived by timer source + auto timer_dstf = DataSpecUtils::asConcreteDataMatcher(workflow[timerCandidate].outputs[0]); + ccdbBackend.inputs.push_back(InputSpec{"tfn", timer_dstf, Lifetime::Timeframe}); } } + ccdbBackend.outputs.push_back(OutputSpec{"CTP", "OrbitReset", 0}); // Load the CCDB backend from the plugin ccdbBackend.algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkCCDBSupport", "CCDBFetcherPlugin", ctx); extraSpecs.push_back(ccdbBackend); - } else { - // If there is no CCDB requested, but we still ask for a FLP/DISTSUBTIMEFRAME/0xccdb - // we add to the first data processor which has no inputs (apart from - // enumerations / timers) the responsibility to provide the DISTSUBTIMEFRAME - bool requiresDISTSUBTIMEFRAME = false; - for (auto& dp : workflow) { - for (auto& input : dp.inputs) { - if (DataSpecUtils::match(input, dstf)) { - requiresDISTSUBTIMEFRAME = true; - break; - } - } - } - if (requiresDISTSUBTIMEFRAME) { - // We find the first device which has either just enumerations or - // just timers, and we add the DISTSUBTIMEFRAME to it. - // Notice how we do so in a stable manner by sorting the devices - // by name. - int enumCandidate = -1; - int timerCandidate = -1; - for (size_t wi = 0; wi < workflow.size(); wi++) { - auto& dp = workflow[wi]; - if (dp.inputs.size() != 1) { - continue; - } - auto lifetime = dp.inputs[0].lifetime; - if (lifetime == Lifetime::Enumeration && (enumCandidate == -1 || workflow[enumCandidate].name > dp.name)) { - enumCandidate = wi; - } - if (lifetime == Lifetime::Timer && (timerCandidate == -1 || workflow[timerCandidate].name > dp.name)) { - timerCandidate = wi; - } - } - if (enumCandidate != -1) { - auto& dp = workflow[enumCandidate]; - DataSpecUtils::updateOutputList(dp.outputs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); - ccdbBackend.inputs.push_back(InputSpec{"tfn", dstf, Lifetime::Timeframe}); - } else if (timerCandidate != -1) { - auto& dp = workflow[timerCandidate]; - dstf = DataSpecUtils::asConcreteDataMatcher(dp.outputs[0]); - ccdbBackend.inputs.push_back(InputSpec{{"tfn"}, dstf, Lifetime::Timeframe}); - } - } + } else if (requiresDISTSUBTIMEFRAME && enumCandidate != -1) { + // add DSTF/ccdb source to the enumeration-driven source explicitly if it is required in the workflow + DataSpecUtils::updateOutputList(workflow[enumCandidate].outputs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); } - // add the Analysys CCDB backend which reads CCDB objects using a provided - // table + // add the Analysys CCDB backend which reads CCDB objects using a provided table if (analysisCCDBBackend.outputs.empty() == false) { - // add normal reader - auto&& algo = PluginManager::loadAlgorithmFromPlugin("O2FrameworkCCDBSupport", "AnalysisCCDBFetcherPlugin", ctx); - analysisCCDBBackend.algorithm = algo; extraSpecs.push_back(analysisCCDBBackend); } @@ -615,7 +523,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // This is to inject a file sink so that any dangling ATSK object is written // to a ROOT file. - if (ac.providedOutputObjHist.empty() == false) { + if (dec.providedOutputObjHist.empty() == false) { auto rootSink = AnalysisSupportHelpers::getOutputObjHistSink(ctx); extraSpecs.push_back(rootSink); } @@ -623,60 +531,24 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); extraSpecs.clear(); - /// Analyze all ouputs - auto [outputsInputsTmp, isDanglingTmp] = analyzeOutputs(workflow); - ac.isDangling = isDanglingTmp; - ac.outputsInputs = outputsInputsTmp; - - // create DataOutputDescriptor - std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); - - // select outputs of type AOD which need to be saved - // ATTENTION: if there are dangling outputs the getGlobalAODSink - // has to be created in any case! - for (auto ii = 0u; ii < ac.outputsInputs.size(); ii++) { - if (DataSpecUtils::partialMatch(ac.outputsInputs[ii], extendedAODOrigins)) { - auto ds = dod->getDataOutputDescriptors(ac.outputsInputs[ii]); - if (ds.size() > 0 || ac.isDangling[ii]) { - ac.outputsInputsAOD.emplace_back(ac.outputsInputs[ii]); - } - } - } - - // file sink for any AOD output - if (ac.outputsInputsAOD.size() > 0) { - // add TFNumber and TFFilename as input to the writer - ac.outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); - ac.outputsInputsAOD.emplace_back(InputSpec{"tff", "TFF", "TFFilename"}); - auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); - extraSpecs.push_back(fileSink); - - auto it = std::find_if(ac.outputsInputs.begin(), ac.outputsInputs.end(), [](InputSpec& spec) -> bool { - return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); - }); - size_t ii = std::distance(ac.outputsInputs.begin(), it); - ac.isDangling[ii] = false; - } - - workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); - extraSpecs.clear(); + injectAODWriter(workflow, ctx); // Select dangling outputs which are not of type AOD std::vector redirectedOutputsInputs; - for (auto ii = 0u; ii < ac.outputsInputs.size(); ii++) { + for (auto ii = 0u; ii < dec.outputsInputs.size(); ii++) { if (ctx.options().get("forwarding-policy") == "none") { continue; } // We forward to the output proxy all the inputs only if they are dangling // or if the forwarding policy is "proxy". - if (!ac.isDangling[ii] && (ctx.options().get("forwarding-policy") != "all")) { + if (!dec.isDangling[ii] && (ctx.options().get("forwarding-policy") != "all")) { continue; } // AODs are skipped in any case. - if (DataSpecUtils::partialMatch(ac.outputsInputs[ii], extendedAODOrigins)) { + if (DataSpecUtils::partialMatch(dec.outputsInputs[ii], extendedAODOrigins)) { continue; } - redirectedOutputsInputs.emplace_back(ac.outputsInputs[ii]); + redirectedOutputsInputs.emplace_back(dec.outputsInputs[ii]); } std::vector unmatched; @@ -699,7 +571,19 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext ignoredInput.lifetime = Lifetime::Sporadic; } - extraSpecs.push_back(CommonDataProcessors::getDummySink(ignored, rateLimitingChannelConfigOutput)); + // Use the new dummy sink when the AOD reader is there + O2_SIGNPOST_ID_GENERATE(sid, workflow_helpers); + if (aodReader.outputs.empty() == false) { + O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "injectServiceDevices", "Injecting scheduled dummy sink"); + extraSpecs.push_back(CommonDataProcessors::getScheduledDummySink(ignored)); + } else { + O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "injectServiceDevices", "Injecting rate limited dummy sink"); + std::string rateLimitingChannelConfigOutput; + if (rateLimitingIPCID != -1) { + rateLimitingChannelConfigOutput = fmt::format("name=metric-feedback,type=push,method=bind,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); + } + extraSpecs.push_back(CommonDataProcessors::getDummySink(ignored, rateLimitingChannelConfigOutput)); + } } workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); @@ -773,15 +657,8 @@ void WorkflowHelpers::adjustTopology(WorkflowSpec& workflow, ConfigContext const } if (distSTFCount > 0) { - bool found = false; for (auto& spec : workflow) { - for (auto& output : spec.outputs) { - if (DataSpecUtils::match(output, ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", 0})) { - found = true; - break; - } - } - if (found) { + if (std::ranges::any_of(spec.outputs, [](auto const& output) { return DataSpecUtils::match(output, ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", 0}); })) { for (unsigned int i = 1; i < distSTFCount; ++i) { spec.outputs.emplace_back(OutputSpec{ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", i}, Lifetime::Timeframe}); } @@ -791,6 +668,41 @@ void WorkflowHelpers::adjustTopology(WorkflowSpec& workflow, ConfigContext const } } +void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext const& ctx) +{ + auto& dec = ctx.services().get(); + /// Analyze all ouputs + std::tie(dec.outputsInputs, dec.isDangling) = analyzeOutputs(workflow); + + // create DataOutputDescriptor + std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); + + // select outputs of type AOD which need to be saved + dec.outputsInputsAOD.clear(); + for (auto ii = 0u; ii < dec.outputsInputs.size(); ii++) { + if (DataSpecUtils::partialMatch(dec.outputsInputs[ii], extendedAODOrigins)) { + auto ds = dod->getDataOutputDescriptors(dec.outputsInputs[ii]); + if (ds.size() > 0 || dec.isDangling[ii]) { + dec.outputsInputsAOD.emplace_back(dec.outputsInputs[ii]); + } + } + } + + // file sink for any AOD output + if (dec.outputsInputsAOD.size() > 0) { + // add TFNumber and TFFilename as input to the writer + DataSpecUtils::updateInputList(dec.outputsInputsAOD, InputSpec{"tfn", "TFN", "TFNumber"}); + DataSpecUtils::updateInputList(dec.outputsInputsAOD, InputSpec{"tff", "TFF", "TFFilename"}); + auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); + workflow.push_back(fileSink); + + auto it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { + return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); + }); + dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; + } +} + void WorkflowHelpers::constructGraph(const WorkflowSpec& workflow, std::vector& logicalEdges, std::vector& outputs, @@ -1084,7 +996,7 @@ std::tuple, std::vector> WorkflowHelpers::analyzeOu input.binding = (snprintf(buf, 63, "output_%zu_%zu", output.workflowId, output.id), buf); // make sure that entries are unique - if (std::find(results.begin(), results.end(), input) == results.end()) { + if (std::ranges::find(results, input) == results.end()) { results.emplace_back(input); isDangling.emplace_back(matched == false); } diff --git a/Framework/Core/src/WorkflowHelpers.h b/Framework/Core/src/WorkflowHelpers.h index b2a4d4cab55df..5c0aa363c6d67 100644 --- a/Framework/Core/src/WorkflowHelpers.h +++ b/Framework/Core/src/WorkflowHelpers.h @@ -182,6 +182,9 @@ struct WorkflowHelpers { // @a ctx the context for the configuration phase static void injectServiceDevices(WorkflowSpec& workflow, ConfigContext& ctx); + // Function to correctly add AOD writer + static void injectAODWriter(WorkflowSpec& workflow, ConfigContext const& ctx); + // Final adjustments to @a workflow after service devices have been injected. static void adjustTopology(WorkflowSpec& workflow, ConfigContext const& ctx); diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index ae6ea03063dfc..815fce47544d0 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. #include +#include "Framework/DanglingEdgesContext.h" #include "Framework/TopologyPolicyHelpers.h" #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include @@ -748,7 +749,11 @@ void spawnDevice(uv_loop_t* loop, for (auto& env : execution.environ) { putenv(strdup(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec).data())); } - execvp(execution.args[0], execution.args.data()); + int err = execvp(execution.args[0], execution.args.data()); + if (err) { + perror("Unable to start child process"); + exit(1); + } } else { O2_SIGNPOST_ID_GENERATE(sid, driver); O2_SIGNPOST_EVENT_EMIT(driver, sid, "spawnDevice", "New child at %{pid}d", id); @@ -812,7 +817,8 @@ void spawnDevice(uv_loop_t* loop, .sendInitialValue = true, }); - for (size_t i = 0; i < DefaultsHelpers::pipelineLength(); ++i) { + unsigned int pipelineLength = DefaultsHelpers::pipelineLength(DeviceConfig{varmap}); + for (size_t i = 0; i < pipelineLength; ++i) { allStates.back().registerState(DataProcessingStates::StateSpec{ .name = fmt::format("matcher_variables/{}", i), .stateId = static_cast((short)(ProcessingStateId::CONTEXT_VARIABLES_BASE) + i), @@ -821,7 +827,7 @@ void spawnDevice(uv_loop_t* loop, }); } - for (size_t i = 0; i < DefaultsHelpers::pipelineLength(); ++i) { + for (size_t i = 0; i < pipelineLength; ++i) { allStates.back().registerState(DataProcessingStates::StateSpec{ .name = fmt::format("data_relayer/{}", i), .stateId = static_cast((short)(ProcessingStateId::DATA_RELAYER_BASE) + i), @@ -1012,6 +1018,7 @@ void doDefaultWorkflowTerminationHook() } int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, + DanglingEdgesContext& danglingEdgesContext, RunningWorkflowInfo const& runningWorkflow, RunningDeviceRef ref, DriverConfig const& driverConfig, @@ -1022,7 +1029,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, fair::Logger::SetConsoleColor(false); fair::Logger::OnFatal([]() { throw runtime_error("Fatal error"); }); DeviceSpec const& spec = runningWorkflow.devices[ref.index]; - LOG(info) << "Spawing new device " << spec.id << " in process with pid " << getpid(); + LOG(info) << "Spawning new device " << spec.id << " in process with pid " << getpid(); fair::mq::DeviceRunner runner{argc, argv}; @@ -1052,6 +1059,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, ("signposts", bpo::value()->default_value(defaultSignposts ? defaultSignposts : ""), "comma separated list of signposts to enable") // ("expected-region-callbacks", bpo::value()->default_value("0"), "how many region callbacks we are expecting") // ("exit-transition-timeout", bpo::value()->default_value(defaultExitTransitionTimeout), "how many second to wait before switching from RUN to READY") // + ("error-on-exit-transition-timeout", bpo::value()->zero_tokens()->default_value(false), "print error instead of warning when exit transition timer expires") // ("data-processing-timeout", bpo::value()->default_value(defaultDataProcessingTimeout), "how many second to wait before stopping data processing and allowing data calibration") // ("timeframes-rate-limit", bpo::value()->default_value("0"), "how many timeframe can be in fly at the same moment (0 disables)") // ("configuration,cfg", bpo::value()->default_value("command-line"), "configuration backend") // @@ -1073,6 +1081,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, &spec, "aEvaluator, &serviceRegistry, + &danglingEdgesContext, &deviceState, &deviceProxy, &processingPolicies, @@ -1091,13 +1100,14 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, quotaEvaluator = std::make_unique(serviceRef); serviceRef.registerService(ServiceRegistryHelpers::handleForService(quotaEvaluator.get())); - deviceContext = std::make_unique(); + deviceContext = std::make_unique(DeviceContext{.processingPolicies = processingPolicies}); serviceRef.registerService(ServiceRegistryHelpers::handleForService(&spec)); serviceRef.registerService(ServiceRegistryHelpers::handleForService(&runningWorkflow)); serviceRef.registerService(ServiceRegistryHelpers::handleForService(deviceContext.get())); serviceRef.registerService(ServiceRegistryHelpers::handleForService(&driverConfig)); + serviceRef.registerService(ServiceRegistryHelpers::handleForService(&danglingEdgesContext)); - auto device = std::make_unique(ref, serviceRegistry, processingPolicies); + auto device = std::make_unique(ref, serviceRegistry); serviceRef.get().setDevice(device.get()); r.fDevice = std::move(device); @@ -1238,6 +1248,7 @@ std::vector getDumpableMetrics() dumpableMetrics.emplace_back("^total-timeframes.*"); dumpableMetrics.emplace_back("^device_state.*"); dumpableMetrics.emplace_back("^total_wall_time_ms$"); + dumpableMetrics.emplace_back("^ccdb-.*$"); return dumpableMetrics; } @@ -1246,8 +1257,10 @@ void dumpMetricsCallback(uv_timer_t* handle) auto* context = (DriverServerContext*)handle->data; static auto performanceMetrics = getDumpableMetrics(); + std::ofstream file(context->driver->resourcesMonitoringFilename, std::ios::out); ResourcesMonitoringHelper::dumpMetricsToJSON(*(context->metrics), - context->driver->metrics, *(context->specs), performanceMetrics); + context->driver->metrics, *(context->specs), performanceMetrics, + file); } void dumpRunSummary(DriverServerContext& context, DriverInfo const& driverInfo, DeviceInfos const& infos, DeviceSpecs const& specs) @@ -1417,6 +1430,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, // We initialise this in the driver, because different drivers might have // different versions of the service ServiceRegistry serviceRegistry; + ServiceRegistryRef::globalDeviceRef(new ServiceRegistryRef{serviceRegistry, ServiceRegistry::globalDeviceSalt()}); if ((driverConfig.batch == false || getenv("DPL_DRIVER_REMOTE_GUI") != nullptr) && frameworkId.empty()) { debugGUI = initDebugGUI(); @@ -1946,6 +1960,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, if (runningWorkflow.devices[di].id == frameworkId) { return doChild(driverInfo.argc, driverInfo.argv, serviceRegistry, + driverInfo.configContext->services().get(), runningWorkflow, ref, driverConfig, driverInfo.processingPolicies, @@ -2034,6 +2049,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, "--fairmq-ipc-prefix", "--readers", "--resources-monitoring", + "--resources-monitoring-file", "--resources-monitoring-dump-interval", "--time-limit", }; @@ -2267,7 +2283,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, if (driverInfo.resourcesMonitoringDumpInterval) { uv_timer_stop(&metricDumpTimer); } - LOG(info) << "Dumping performance metrics to performanceMetrics.json file"; + LOGP(info, "Dumping performance metrics to {}.json file", driverInfo.resourcesMonitoringFilename); dumpMetricsCallback(&metricDumpTimer); } dumpRunSummary(serverContext, driverInfo, infos, runningWorkflow.devices); @@ -2915,6 +2931,7 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, ("no-IPC", bpo::value()->zero_tokens()->default_value(false), "disable IPC topology optimization") // // ("o2-control,o2", bpo::value()->default_value(""), "dump O2 Control workflow configuration under the specified name") // ("resources-monitoring", bpo::value()->default_value(0), "enable cpu/memory monitoring for provided interval in seconds") // + ("resources-monitoring-file", bpo::value()->default_value("performanceMetrics.json"), "file where to dump the metrics") // ("resources-monitoring-dump-interval", bpo::value()->default_value(0), "dump monitoring information to disk every provided seconds"); // // some of the options must be forwarded by default to the device executorOptions.add(DeviceSpecHelpers::getForwardedDeviceOptions()); @@ -2996,8 +3013,8 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, ServiceSpecs driverServices = ServiceSpecHelpers::filterDisabled(CommonDriverServices::defaultServices(), driverServicesOverride); // We insert the hash for the internal devices. WorkflowHelpers::injectServiceDevices(physicalWorkflow, configContext); - auto reader = std::find_if(physicalWorkflow.begin(), physicalWorkflow.end(), [](DataProcessorSpec& spec) { return spec.name == "internal-dpl-aod-reader"; }); - if (reader != physicalWorkflow.end()) { + auto& dec = configContext.services().get(); + if (!(dec.requestedAODs.empty() && dec.requestedDYNs.empty() && dec.requestedIDXs.empty() && dec.requestedTIMs.empty())) { driverServices.push_back(ArrowSupport::arrowBackendSpec()); } for (auto& service : driverServices) { @@ -3185,6 +3202,7 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, driverInfo.deployHostname = varmap["hostname"].as(); driverInfo.resources = varmap["resources"].as(); driverInfo.resourcesMonitoringInterval = varmap["resources-monitoring"].as(); + driverInfo.resourcesMonitoringFilename = varmap["resources-monitoring-file"].as(); driverInfo.resourcesMonitoringDumpInterval = varmap["resources-monitoring-dump-interval"].as(); // FIXME: should use the whole dataProcessorInfos, actually... diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index dcff3930dbaad..3c3d2294fdd7e 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -65,7 +65,7 @@ static void BM_RelaySingleSlot(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -118,7 +118,7 @@ static void BM_RelayMultipleSlots(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -177,7 +177,7 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -254,7 +254,7 @@ static void BM_RelaySplitParts(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -314,7 +314,7 @@ static void BM_RelayMultiplePayloads(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // DataHeader matching the one provided in the input diff --git a/Framework/Core/test/benchmark_EventMixing.cxx b/Framework/Core/test/benchmark_EventMixing.cxx index 99a7d0d4b1cb9..0e7e6839ee35e 100644 --- a/Framework/Core/test/benchmark_EventMixing.cxx +++ b/Framework/Core/test/benchmark_EventMixing.cxx @@ -78,7 +78,8 @@ static void BM_EventMixingTraditional(benchmark::State& state) auto tableTrack = trackBuilder.finalize(); o2::aod::StoredTracks tracks{tableTrack}; - ArrowTableSlicingCache atscache({{getLabelFromType(), "fIndex" + cutString(getLabelFromType())}}); + std::string key = "fIndex" + cutString(getLabelFromType()); + ArrowTableSlicingCache atscache({{getLabelFromType(), getMatcherFromTypeForKey(key), key}}); auto s = atscache.updateCacheEntry(0, tableTrack); SliceCache cache{&atscache}; @@ -171,7 +172,8 @@ static void BM_EventMixingCombinations(benchmark::State& state) int64_t count = 0; int64_t colCount = 0; - ArrowTableSlicingCache atscache{{{getLabelFromType(), "fIndex" + getLabelFromType()}}}; + std::string key = "fIndex" + getLabelFromType(); + ArrowTableSlicingCache atscache{{{getLabelFromType(), getMatcherFromTypeForKey(key), key}}}; auto s = atscache.updateCacheEntry(0, tableTrack); SliceCache cache{&atscache}; diff --git a/Framework/Core/test/test_ASoA.cxx b/Framework/Core/test/test_ASoA.cxx index 80519aebc9ee7..117dddff4c548 100644 --- a/Framework/Core/test/test_ASoA.cxx +++ b/Framework/Core/test/test_ASoA.cxx @@ -1187,7 +1187,8 @@ TEST_CASE("TestSliceByCached") auto refs = w.finalize(); o2::aod::References r{refs}; - ArrowTableSlicingCache atscache({{o2::soa::getLabelFromType(), "fIndex" + o2::framework::cutString(o2::soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(o2::soa::getLabelFromType()); + ArrowTableSlicingCache atscache({{o2::soa::getLabelFromType(), o2::soa::getMatcherFromTypeForKey(key), key}}); auto s = atscache.updateCacheEntry(0, refs); SliceCache cache{&atscache}; @@ -1238,7 +1239,7 @@ TEST_CASE("TestSliceByCachedMismatched") J rr{{refs, refs2}}; auto key = "fIndex" + o2::framework::cutString(o2::soa::getLabelFromType()) + "_alt"; - ArrowTableSlicingCache atscache({{o2::soa::getLabelFromTypeForKey(key), key}}); + ArrowTableSlicingCache atscache({{o2::soa::getLabelFromTypeForKey(key), o2::soa::getMatcherFromTypeForKey(key), key}}); auto s = atscache.updateCacheEntry(0, refs2); SliceCache cache{&atscache}; diff --git a/Framework/Core/test/test_AllCrashTypes.sh b/Framework/Core/test/test_AllCrashTypes.sh index 54898fd9c4c5d..d333cf4252816 100755 --- a/Framework/Core/test/test_AllCrashTypes.sh +++ b/Framework/Core/test/test_AllCrashTypes.sh @@ -1,23 +1,23 @@ #!/bin/sh -e echo $PATH printf "ok\nTesting runtime-init..." -o2-framework-crashing-workflow --crash-type=runtime-init --completion-policy=quit -b --run | grep -q "Exception caught while in Init: This is a std::runtime_error. Exiting with 1." || { printf "runtime error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=runtime-init --completion-policy=quit -b --run | tee error.log | grep -q "Exception caught while in Init: This is a std::runtime_error. Exiting with 1." || { printf "runtime error not found" ; cat error.log ; exit 1; } printf "ok\nTesting framework-init..." -o2-framework-crashing-workflow --crash-type=framework-init --completion-policy=quit -b --run | grep -q "Exception caught while in Init: This is a o2::framework::runtime_error. Exiting with 1." || { printf "framework error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=framework-init --completion-policy=quit -b --run | tee error.log | grep -q "Exception caught while in Init: This is a o2::framework::runtime_error. Exiting with 1." || { printf "framework error not found" ; cat error.log ; exit 1; } printf "ok\nTesting framework-run..." -o2-framework-crashing-workflow --crash-type=framework-run --completion-policy=quit -b --run | grep -q "Unhandled o2::framework::runtime_error reached the top of main of o2-framework-crashing-workflow, device shutting down. Reason: This is a o2::framework::runtime_error" || { printf "framework error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=framework-run --completion-policy=quit -b --run | tee error.log | grep -q "Unhandled o2::framework::runtime_error reached the top of main of [^ ]*o2-framework-crashing-workflow, device shutting down. Reason: This is a o2::framework::runtime_error" || { printf "framework error not found" ; cat error.log ; exit 1; } printf "ok\nTesting runtime-run..." -o2-framework-crashing-workflow --crash-type=runtime-run --completion-policy=quit --run | grep -q "Unhandled o2::framework::runtime_error reached the top of main of o2-framework-crashing-workflow, device shutting down. Reason: This is a std::runtime_error" || { echo "runtime error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=runtime-run --completion-policy=quit --run | tee error.log | grep -q "Unhandled o2::framework::runtime_error reached the top of main of [^ ]*o2-framework-crashing-workflow, device shutting down. Reason: This is a std::runtime_error" || { echo "runtime error not found" ; cat error.log ; exit 1; } printf "ok\n" export O2_NO_CATCHALL_EXCEPTIONS=1 echo O2_NO_CATCHALL_EXCEPTIONS enabled printf "ok\nTesting runtime-init..." -o2-framework-crashing-workflow --crash-type=runtime-init --completion-policy=quit -b --run | grep -v -q "Exception caught: This is a std::runtime_error" || { printf "runtime error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=runtime-init --completion-policy=quit -b --run | tee error.log | grep -v -q "Exception caught: This is a std::runtime_error" || { printf "runtime error not found" ; cat error.log ; exit 1; } printf "ok\nTesting framework-init..." -o2-framework-crashing-workflow --crash-type=framework-init --completion-policy=quit -b --run | grep -v -q "Exception caught: This is a o2::framework::runtime_error" || { printf "framework error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=framework-init --completion-policy=quit -b --run | tee error.log | grep -v -q "Exception caught: This is a o2::framework::runtime_error" || { printf "framework error not found" ; cat error.log ; exit 1; } printf "ok\nTesting framework-run..." -o2-framework-crashing-workflow --crash-type=framework-run --completion-policy=quit -b --run | grep -v -q "Unhandled o2::framework::runtime_error reached the top of main of o2-framework-crashing-workflow, device shutting down. Reason: This is a o2::framework::runtime_error" || { printf "framework error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=framework-run --completion-policy=quit -b --run | tee error.log | grep -v -q "Unhandled o2::framework::runtime_error reached the top of main of [^ ]*o2-framework-crashing-workflow, device shutting down. Reason: This is a o2::framework::runtime_error" || { printf "framework error not found" ; cat error.log ; exit 1; } printf "ok\nTesting runtime-run..." -o2-framework-crashing-workflow --crash-type=runtime-run --completion-policy=quit --run | grep -v -q "Unhandled o2::framework::runtime_error reached the top of main of o2-framework-crashing-workflow, device shutting down. Reason: This is a std::runtime_error" || { echo "runtime error not found" ; exit 1; } +o2-framework-crashing-workflow --crash-type=runtime-run --completion-policy=quit --run | tee error.log | grep -v -q "Unhandled o2::framework::runtime_error reached the top of main of [^ ]*o2-framework-crashing-workflow, device shutting down. Reason: This is a std::runtime_error" || { echo "runtime error not found"; cat error.log ; exit 1; } printf "ok" diff --git a/Framework/Core/test/test_ComputingQuotaEvaluator.cxx b/Framework/Core/test/test_ComputingQuotaEvaluator.cxx index 0df58ae7fed08..afd252ec169d4 100644 --- a/Framework/Core/test/test_ComputingQuotaEvaluator.cxx +++ b/Framework/Core/test/test_ComputingQuotaEvaluator.cxx @@ -28,7 +28,7 @@ TEST_CASE("TestComputingQuotaEvaluator") }; ComputingQuotaConsumer dispose2MB = [bs = 2000000](int taskId, - std::array& offers, + std::array& offers, ComputingQuotaStats& stats, std::function accountDisposed) { ComputingQuotaOffer disposed; @@ -51,7 +51,7 @@ TEST_CASE("TestComputingQuotaEvaluator") }; ComputingQuotaConsumer dispose10MB = [bs = 10000000](int taskId, - std::array& offers, + std::array& offers, ComputingQuotaStats& stats, std::function accountDisposed) { ComputingQuotaOffer disposed; diff --git a/Framework/Core/test/test_Concepts.cxx b/Framework/Core/test/test_Concepts.cxx index ea94c4dfffe5a..982c748e701e4 100644 --- a/Framework/Core/test/test_Concepts.cxx +++ b/Framework/Core/test/test_Concepts.cxx @@ -174,6 +174,7 @@ TEST_CASE("IdentificationConcepts") REQUIRE(is_configurable_axis); REQUIRE(is_process_configurable); + REQUIRE(is_process_configurable); struct : ConfigurableGroup { Configurable c{"", 1, ""}; diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 7d5a3ded88e16..8957e361cb8a2 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -83,7 +83,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -133,7 +133,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -195,7 +195,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAll(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -276,7 +276,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAll(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(3); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -359,7 +359,7 @@ TEST_CASE("DataRelayer") std::vector infos{1}; TimesliceIndex index{1, infos}; ref.registerService(ServiceRegistryHelpers::handleForService(&index)); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(2); @@ -437,7 +437,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(2); @@ -509,7 +509,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(3); @@ -568,7 +568,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(1); @@ -629,7 +629,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(1); @@ -698,7 +698,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); DataHeader dh{"CLUSTERS", "TPC", 0}; @@ -752,7 +752,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); diff --git a/Framework/Core/test/test_Expressions.cxx b/Framework/Core/test/test_Expressions.cxx index 4c6fc51795ca8..b4a65fb0c7b48 100644 --- a/Framework/Core/test/test_Expressions.cxx +++ b/Framework/Core/test/test_Expressions.cxx @@ -12,6 +12,7 @@ #include "Framework/Configurable.h" #include "Framework/ExpressionHelpers.h" #include "Framework/AnalysisDataModel.h" +#include "../src/ExpressionJSONHelpers.h" #include #include #include @@ -391,3 +392,95 @@ TEST_CASE("TestStringExpressionsParsing") REQUIRE(tree1c->ToString() == tree2c->ToString()); } + +TEST_CASE("TestExpressionSerialization") +{ + Filter f = o2::aod::track::signed1Pt > 0.f && ifnode(nabs(o2::aod::track::eta) < 1.0f, nabs(o2::aod::track::x) > 2.0f, nabs(o2::aod::track::y) > 3.0f); + Projector p = -1.f * nlog(ntan(o2::constants::math::PIQuarter - 0.5f * natan(o2::aod::fwdtrack::tgl))); + Projector p1 = ifnode(o2::aod::track::itsClusterSizes > (uint32_t)0, static_cast(o2::aod::track::ITS), (uint8_t)0x0) | + ifnode(o2::aod::track::tpcNClsFindable > (uint8_t)0, static_cast(o2::aod::track::TPC), (uint8_t)0x0) | + ifnode(o2::aod::track::trdPattern > (uint8_t)0, static_cast(o2::aod::track::TRD), (uint8_t)0x0) | + ifnode((o2::aod::track::tofChi2 >= 0.f) && (o2::aod::track::tofExpMom > 0.f), static_cast(o2::aod::track::TOF), (uint8_t)0x0); + + std::vector projectors; + projectors.emplace_back(std::move(f)); + projectors.emplace_back(std::move(p)); + projectors.emplace_back(std::move(p1)); + + std::stringstream osm; + ExpressionJSONHelpers::write(osm, projectors); + + std::stringstream ism; + ism.str(osm.str()); + auto ps = ExpressionJSONHelpers::read(ism); + + auto s1 = createOperations(projectors[0]); + auto s2 = createOperations(ps[0]); + auto schemaf = std::make_shared(std::vector{o2::aod::track::Eta::asArrowField(), o2::aod::track::Signed1Pt::asArrowField(), o2::aod::track::X::asArrowField(), o2::aod::track::Y::asArrowField()}); + auto t1 = createExpressionTree(s1, schemaf); + auto t2 = createExpressionTree(s2, schemaf); + REQUIRE(t1->ToString() == t2->ToString()); + + auto s12 = createOperations(projectors[1]); + auto s22 = createOperations(ps[1]); + auto schemap = std::make_shared(std::vector{o2::aod::fwdtrack::Tgl::asArrowField()}); + auto t12 = createExpressionTree(s12, schemap); + auto t22 = createExpressionTree(s22, schemap); + REQUIRE(t12->ToString() == t22->ToString()); + + auto s13 = createOperations(projectors[2]); + auto s23 = createOperations(ps[2]); + auto schemap1 = std::make_shared(std::vector{o2::aod::track::ITSClusterSizes::asArrowField(), o2::aod::track::TPCNClsFindable::asArrowField(), + o2::aod::track::TRDPattern::asArrowField(), o2::aod::track::TOFChi2::asArrowField(), + o2::aod::track::TOFExpMom::asArrowField()}); + auto t13 = createExpressionTree(s13, schemap1); + auto t23 = createExpressionTree(s23, schemap1); + REQUIRE(t13->ToString() == t23->ToString()); + + osm.clear(); + osm.str(""); + ArrowJSONHelpers::write(osm, schemaf); + + ism.clear(); + ism.str(osm.str()); + auto newSchemaf = ArrowJSONHelpers::read(ism); + REQUIRE(schemaf->ToString() == newSchemaf->ToString()); + + osm.clear(); + osm.str(""); + ArrowJSONHelpers::write(osm, schemap); + + ism.clear(); + ism.str(osm.str()); + auto newSchemap = ArrowJSONHelpers::read(ism); + REQUIRE(schemap->ToString() == newSchemap->ToString()); + + osm.clear(); + osm.str(""); + ArrowJSONHelpers::write(osm, schemap1); + + ism.clear(); + ism.str(osm.str()); + auto newSchemap1 = ArrowJSONHelpers::read(ism); + REQUIRE(schemap1->ToString() == newSchemap1->ToString()); + + osm.clear(); + osm.str(""); + auto realisticSchema = std::make_shared(o2::soa::createFieldsFromColumns(o2::aod::MetadataTrait>::metadata::persistent_columns_t{})); + ArrowJSONHelpers::write(osm, realisticSchema); + + ism.clear(); + ism.str(osm.str()); + auto restoredSchema = ArrowJSONHelpers::read(ism); + REQUIRE(realisticSchema->ToString() == restoredSchema->ToString()); + + osm.clear(); + osm.str(""); + auto realisticSchema1 = std::make_shared(o2::soa::createFieldsFromColumns(o2::aod::MetadataTrait>::metadata::persistent_columns_t{})); + ArrowJSONHelpers::write(osm, realisticSchema1); + + ism.clear(); + ism.str(osm.str()); + auto restoredSchema1 = ArrowJSONHelpers::read(ism); + REQUIRE(realisticSchema1->ToString() == restoredSchema1->ToString()); +} diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx new file mode 100644 index 0000000000000..7081d600080b1 --- /dev/null +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -0,0 +1,781 @@ +// 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 +#include "Headers/DataHeader.h" +#include "Framework/DataProcessingHeader.h" +#include "Framework/DataProcessingHelpers.h" +#include "Framework/SourceInfoHeader.h" +#include "Framework/DomainInfoHeader.h" +#include "Framework/Signpost.h" +#include "Framework/MessageSet.h" +#include "Framework/FairMQDeviceProxy.h" +#include "Headers/Stack.h" +#include "MemoryResources/MemoryResources.h" +#include +#include +#include + +O2_DECLARE_DYNAMIC_LOG(forwarding); +using namespace o2::framework; + +TEST_CASE("ForwardInputsEmpty") +{ + o2::header::DataHeader dh; + dh.dataDescription = "CLUSTERS"; + dh.dataOrigin = "TPC"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + + std::vector currentSetOfInputs; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.empty()); +} + +TEST_CASE("ForwardInputsSingleMessageSingleRoute") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + std::vector channels{ + fair::mq::Channel("from_A_to_B")}; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 2); // Two messages for that route +} + +TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + std::vector channels{ + fair::mq::Channel("from_A_to_B")}; + + bool copyByDefault = false; + FairMQDeviceProxy proxy; + std::vector routes{ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(nullptr); + REQUIRE(payload.get() == nullptr); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, true); + REQUIRE(result.size() == 1); + REQUIRE(result[0].Size() == 0); // Because there is a nullptr, we do not forward this as it was already consumed. +} + +TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + o2::framework::SourceInfoHeader sih{}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B")}; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, sih}); + REQUIRE(o2::header::get(header->GetData())); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 0); // FIXME: this is an actual error. It should be 2. However it cannot really happen. + // Correct behavior below: + // REQUIRE(result[0].Size() == 2); + // REQUIRE(o2::header::get(result[0].At(0)->GetData()) == nullptr); +} + +TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + o2::framework::DomainInfoHeader dih{}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B")}; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, dih}); + REQUIRE(o2::header::get(header->GetData())); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 0); // FIXME: this is actually wrong + // FIXME: actually correct behavior below + // REQUIRE(result[0].Size() == 2); // Two messages + // REQUIRE(o2::header::get(result[0].At(0)->GetData()) == nullptr); // it should not have the end of stream +} + +TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B"), + fair::mq::Channel("from_A_to_C"), + }; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }, + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding2", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_C", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + REQUIRE(result[0].Size() == 2); // Two messages per route + REQUIRE(result[1].Size() == 0); // Only the first DPL matched channel matters +} + +TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + std::vector channels{ + fair::mq::Channel("external"), + fair::mq::Channel("from_A_to_C"), + }; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "external", + .policy = nullptr, + }, + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding2", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_C", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + REQUIRE(result[0].Size() == 2); // With external matching channels, we need to copy and then forward + REQUIRE(result[1].Size() == 2); // +} + +TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") +{ + o2::header::DataHeader dh1; + dh1.dataOrigin = "TST"; + dh1.dataDescription = "A"; + dh1.subSpecification = 0; + dh1.splitPayloadIndex = 0; + dh1.splitPayloadParts = 1; + + o2::header::DataHeader dh2; + dh2.dataOrigin = "TST"; + dh2.dataDescription = "B"; + dh2.subSpecification = 0; + dh2.splitPayloadIndex = 0; + dh2.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B"), + fair::mq::Channel("from_A_to_C"), + }; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }, + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding2", ConcreteDataMatcher{"TST", "B", 0}}, + .channel = "from_A_to_C", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload1(transport->CreateMessage()); + fair::mq::MessagePtr payload2(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header1 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh1, dph}); + MessageSet messageSet1; + messageSet1.add(PartRef{std::move(header1), std::move(payload1)}); + REQUIRE(messageSet1.size() == 1); + + auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); + MessageSet messageSet2; + messageSet2.add(PartRef{std::move(header2), std::move(payload2)}); + REQUIRE(messageSet2.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet1)); + currentSetOfInputs.emplace_back(std::move(messageSet2)); + REQUIRE(currentSetOfInputs.size() == 2); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + REQUIRE(result[0].Size() == 2); // + REQUIRE(result[1].Size() == 2); // +} + +TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 0; + dh.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B"), + fair::mq::Channel("from_A_to_C"), + }; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "B", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }, + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_C", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + REQUIRE(result[0].Size() == 0); // Two messages per route + REQUIRE(result[1].Size() == 2); // Two messages per route +} + +TEST_CASE("ForwardInputsSplitPayload") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 2; + dh.splitPayloadParts = 2; + + o2::header::DataHeader dh2; + dh2.dataOrigin = "TST"; + dh2.dataDescription = "B"; + dh2.subSpecification = 0; + dh2.splitPayloadIndex = 0; + dh2.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B"), + fair::mq::Channel("from_A_to_C"), + }; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "B", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }, + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_C", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload1(transport->CreateMessage()); + fair::mq::MessagePtr payload2(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + std::vector> messages; + messages.push_back(std::move(header)); + messages.push_back(std::move(payload1)); + messages.push_back(std::move(payload2)); + auto fillMessages = [&messages](size_t t) -> fair::mq::MessagePtr { + return std::move(messages[t]); + }; + messageSet.add(fillMessages, 3); + auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); + PartRef part{std::move(header2), transport->CreateMessage()}; + messageSet.add(std::move(part)); + + REQUIRE(messageSet.size() == 2); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + CHECK(result[0].Size() == 2); // No messages on this route + CHECK(result[1].Size() == 3); +} + +TEST_CASE("ForwardInputsSplitPayloadNoMessageSet") +{ + o2::header::DataHeader dh; + dh.dataOrigin = "TST"; + dh.dataDescription = "A"; + dh.subSpecification = 0; + dh.splitPayloadIndex = 2; + dh.splitPayloadParts = 2; + + o2::header::DataHeader dh2; + dh2.dataOrigin = "TST"; + dh2.dataDescription = "B"; + dh2.subSpecification = 0; + dh2.splitPayloadIndex = 0; + dh2.splitPayloadParts = 1; + + o2::framework::DataProcessingHeader dph{0, 1}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B"), + fair::mq::Channel("from_A_to_C"), + }; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "B", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }, + ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_C", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload1(transport->CreateMessage()); + fair::mq::MessagePtr payload2(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + std::vector> messages; + messages.push_back(std::move(header)); + messages.push_back(std::move(payload1)); + messages.push_back(std::move(payload2)); + auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); + messages.push_back(std::move(header2)); + messages.push_back(transport->CreateMessage()); + + std::vector result(2); + auto span = std::span(messages); + o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, span, result, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + CHECK(result[0].Size() == 2); // No messages on this route + CHECK(result[1].Size() == 3); +} + +TEST_CASE("ForwardInputEOSSingleRoute") +{ + o2::framework::SourceInfoHeader sih{}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B")}; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, sih}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 0); // Oldest possible timeframe should not be forwarded +} + +TEST_CASE("ForwardInputOldestPossibleSingleRoute") +{ + o2::framework::DomainInfoHeader dih{}; + + std::vector channels{ + fair::mq::Channel("from_A_to_B")}; + + bool consume = true; + bool copyByDefault = true; + FairMQDeviceProxy proxy; + std::vector routes{ForwardRoute{ + .timeslice = 0, + .maxTimeslices = 1, + .matcher = {"binding", ConcreteDataMatcher{"TST", "A", 0}}, + .channel = "from_A_to_B", + .policy = nullptr, + }}; + + auto findChannelByName = [&channels](std::string const& channelName) -> fair::mq::Channel& { + for (auto& channel : channels) { + if (channel.GetName() == channelName) { + return channel; + } + } + throw std::runtime_error("Channel not found"); + }; + + proxy.bind({}, {}, routes, findChannelByName, nullptr); + + std::vector currentSetOfInputs; + MessageSet messageSet; + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dih}); + messageSet.add(PartRef{std::move(header), std::move(payload)}); + REQUIRE(messageSet.size() == 1); + currentSetOfInputs.emplace_back(std::move(messageSet)); + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 0); // Oldest possible timeframe should not be forwarded +} diff --git a/Framework/Core/test/test_GroupSlicer.cxx b/Framework/Core/test/test_GroupSlicer.cxx index 2f21d7dd17975..71360f736c3fb 100644 --- a/Framework/Core/test/test_GroupSlicer.cxx +++ b/Framework/Core/test/test_GroupSlicer.cxx @@ -117,7 +117,8 @@ TEST_CASE("GroupSlicerOneAssociated") REQUIRE(t.size() == 10 * 20); auto tt = std::make_tuple(t); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntry(0, trkTable); o2::framework::GroupSlicer g(e, tt, slices); @@ -191,9 +192,9 @@ TEST_CASE("GroupSlicerSeveralAssociated") auto tt = std::make_tuple(tx, ty, tz, tu); auto key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), key}, - {soa::getLabelFromType(), key}, - {soa::getLabelFromType(), key}}); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}, + {soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}, + {soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntry(0, {trkTableX}); s = slices.updateCacheEntry(1, {trkTableY}); s = slices.updateCacheEntry(2, {trkTableZ}); @@ -256,7 +257,8 @@ TEST_CASE("GroupSlicerMismatchedGroups") REQUIRE(t.size() == 10 * (20 - 5)); auto tt = std::make_tuple(t); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntry(0, trkTable); o2::framework::GroupSlicer g(e, tt, slices); @@ -312,7 +314,8 @@ TEST_CASE("GroupSlicerMismatchedUnassignedGroups") REQUIRE(t.size() == (30 + 10 * (20 - 5))); auto tt = std::make_tuple(t); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntry(0, trkTable); o2::framework::GroupSlicer g(e, tt, slices); @@ -362,7 +365,8 @@ TEST_CASE("GroupSlicerMismatchedFilteredGroups") REQUIRE(t.size() == 10 * (20 - 4)); auto tt = std::make_tuple(t); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntry(0, trkTable); o2::framework::GroupSlicer g(e, tt, slices); @@ -423,7 +427,8 @@ TEST_CASE("GroupSlicerMismatchedUnsortedFilteredGroups") REQUIRE(t.size() == 10 * (20 - 4)); auto tt = std::make_tuple(t); - ArrowTableSlicingCache slices({}, {{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({}, {{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntryUnsorted(0, trkTable); o2::framework::GroupSlicer g(e, tt, slices); @@ -547,8 +552,9 @@ TEST_CASE("GroupSlicerMismatchedUnsortedFilteredGroupsWithSelfIndex") } FilteredParts fp{{partsTable}, rows}; auto associatedTuple = std::make_tuple(fp, t); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}, - {soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}, + {soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s0 = slices.updateCacheEntry(0, partsTable); auto s1 = slices.updateCacheEntry(1, thingsTable); o2::framework::GroupSlicer g(e, associatedTuple, slices); @@ -607,7 +613,8 @@ TEST_CASE("EmptySliceables") REQUIRE(t.size() == 0); auto tt = std::make_tuple(t); - ArrowTableSlicingCache slices({{soa::getLabelFromType(), "fIndex" + o2::framework::cutString(soa::getLabelFromType())}}); + std::string key = "fIndex" + o2::framework::cutString(soa::getLabelFromType()); + ArrowTableSlicingCache slices({{soa::getLabelFromType(), soa::getMatcherFromTypeForKey(key), key}}); auto s = slices.updateCacheEntry(0, trkTable); o2::framework::GroupSlicer g(e, tt, slices); @@ -679,7 +686,7 @@ TEST_CASE("ArrowDirectSlicing") std::vector slices; std::vector offsts; - auto bk = Entry(soa::getLabelFromType(), "fID"); + auto bk = Entry(soa::getLabelFromType(), soa::getMatcherFromTypeForKey("fID"), "fID"); ArrowTableSlicingCache cache({bk}); auto s = cache.updateCacheEntry(0, {evtTable}); auto lcache = cache.getCacheFor(bk); @@ -737,7 +744,7 @@ TEST_CASE("TestSlicingException") } auto evtTable = builderE.finalize(); - auto bk = Entry(soa::getLabelFromType(), "fID"); + auto bk = Entry(soa::getLabelFromType(), soa::getMatcherFromTypeForKey("fID"), "fID"); ArrowTableSlicingCache cache({bk}); try { diff --git a/Framework/Core/test/test_IndexBuilder.cxx b/Framework/Core/test/test_IndexBuilder.cxx index ea9f715f20c8a..e357b1164af80 100644 --- a/Framework/Core/test/test_IndexBuilder.cxx +++ b/Framework/Core/test/test_IndexBuilder.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/AnalysisDataModel.h" -#include "Framework/AnalysisTask.h" +#include "../src/IndexJSONHelpers.h" #include using namespace o2::framework; @@ -102,8 +102,11 @@ TEST_CASE("TestIndexBuilder") auto t4 = b4.finalize(); Categorys st4{t4}; - using m1 = MetadataTrait>::metadata; - auto t5 = IndexBuilder::indexBuilder("test1a", {t1, t2, t3, t4}, typename IDXs::persistent_columns_t{}); + auto map = getIndexMapping>::metadata>(); + auto schema1 = o2::aod::MetadataTrait>::metadata::getSchema(); + std::vector builders1; + auto t5 = IndexBuilder::materialize(builders1, {t1, t2, t3, t4}, map, schema1, true); + // auto t5 = IndexBuilder::materialize({t1, t2, t3, t4}, map, schema1, true); REQUIRE(t5->num_rows() == 4); IDXs idxt{t5}; idxt.bindExternalIndices(&st1, &st2, &st3, &st4); @@ -113,8 +116,10 @@ TEST_CASE("TestIndexBuilder") REQUIRE(row.category().pointId() == row.pointId()); } - using m2 = MetadataTrait>::metadata; - auto t6 = IndexBuilder::indexBuilder("test3", {t2, t1, t3, t4}, typename IDX2s::persistent_columns_t{}); + map = getIndexMapping>::metadata>(); + auto schema2 = o2::aod::MetadataTrait>::metadata::getSchema(); + std::vector builders2; + auto t6 = IndexBuilder::materialize(builders2, {t2, t1, t3, t4}, map, schema2, false); REQUIRE(t6->num_rows() == st2.size()); IDX2s idxs{t6}; std::array fs{0, 1, 2, -1, -1, 4, -1}; @@ -212,8 +217,10 @@ TEST_CASE("AdvancedIndexTables") {14, 34}, {8, 31, 42, 46, 58}}}; - using m3 = MetadataTrait>::metadata; - auto t3 = IndexBuilder::indexBuilder("test4", {t1, t2, tc}, typename IDX3s::persistent_columns_t{}); + auto map = getIndexMapping>::metadata>(); + auto schema3 = o2::aod::MetadataTrait>::metadata::getSchema(); + std::vector builders3; + auto t3 = IndexBuilder::materialize(builders3, {t1, t2, tc}, map, schema3, false); REQUIRE(t3->num_rows() == st1.size()); IDX3s idxs{t3}; idxs.bindExternalIndices(&st1, &st2, &st3); @@ -235,3 +242,38 @@ TEST_CASE("AdvancedIndexTables") ++count; } } + +TEST_CASE("IndexRecordsSerialization") +{ + auto map = getIndexMapping>::metadata>(); + + std::stringstream osm; + IndexJSONHelpers::write(osm, map); + + std::stringstream ism; + ism.str(osm.str()); + auto rmap = IndexJSONHelpers::read(ism); + REQUIRE(map == rmap); + + map = getIndexMapping>::metadata>(); + + osm.clear(); + osm.str(""); + IndexJSONHelpers::write(osm, map); + + ism.clear(); + ism.str(osm.str()); + rmap = IndexJSONHelpers::read(ism); + REQUIRE(map == rmap); + + map = getIndexMapping>::metadata>(); + + osm.clear(); + osm.str(""); + IndexJSONHelpers::write(osm, map); + + ism.clear(); + ism.str(osm.str()); + rmap = IndexJSONHelpers::read(ism); + REQUIRE(map == rmap); +} diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx new file mode 100644 index 0000000000000..d56e32fea1adb --- /dev/null +++ b/Framework/Core/test/test_MessageSet.cxx @@ -0,0 +1,135 @@ +// 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 +#include "Framework/MessageSet.h" +#include + +using namespace o2::framework; + +TEST_CASE("MessageSet") { + o2::framework::MessageSet msgSet; + std::vector ptrs; + std::unique_ptr msg(nullptr); + std::unique_ptr msg2(nullptr); + ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(msg2)); + msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + + REQUIRE(msgSet.messages.size() == 2); + REQUIRE(msgSet.messageMap.size() == 1); + REQUIRE(msgSet.pairMap.size() == 1); + REQUIRE(msgSet.messageMap[0].position == 0); + REQUIRE(msgSet.messageMap[0].size == 1); + + REQUIRE(msgSet.pairMap[0].partIndex == 0); + REQUIRE(msgSet.pairMap[0].payloadIndex == 0); +} + +TEST_CASE("MessageSetWithFunction") { + std::vector ptrs; + std::unique_ptr msg(nullptr); + std::unique_ptr msg2(nullptr); + ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(msg2)); + o2::framework::MessageSet msgSet([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + + REQUIRE(msgSet.messages.size() == 2); + REQUIRE(msgSet.messageMap.size() == 1); + REQUIRE(msgSet.pairMap.size() == 1); + REQUIRE(msgSet.messageMap[0].position == 0); + REQUIRE(msgSet.messageMap[0].size == 1); + + REQUIRE(msgSet.pairMap[0].partIndex == 0); + REQUIRE(msgSet.pairMap[0].payloadIndex == 0); +} + +TEST_CASE("MessageSetWithMultipart") { + std::vector ptrs; + std::unique_ptr msg(nullptr); + std::unique_ptr msg2(nullptr); + std::unique_ptr msg3(nullptr); + ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(msg2)); + ptrs.emplace_back(std::move(msg3)); + o2::framework::MessageSet msgSet([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 3); + + REQUIRE(msgSet.messages.size() == 3); + REQUIRE(msgSet.messageMap.size() == 1); + REQUIRE(msgSet.pairMap.size() == 2); + REQUIRE(msgSet.messageMap[0].position == 0); + REQUIRE(msgSet.messageMap[0].size == 2); + + REQUIRE(msgSet.pairMap[0].partIndex == 0); + REQUIRE(msgSet.pairMap[0].payloadIndex == 0); + REQUIRE(msgSet.pairMap[1].partIndex == 0); + REQUIRE(msgSet.pairMap[1].payloadIndex == 1); +} + +TEST_CASE("MessageSetAddPartRef") { + std::vector ptrs; + std::unique_ptr msg(nullptr); + std::unique_ptr msg2(nullptr); + ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(msg2)); + PartRef ref {std::move(msg), std::move(msg2)}; + o2::framework::MessageSet msgSet; + msgSet.add(std::move(ref)); + + REQUIRE(msgSet.messages.size() == 2); + REQUIRE(msgSet.messageMap.size() == 1); + REQUIRE(msgSet.pairMap.size() == 1); + REQUIRE(msgSet.messageMap[0].position == 0); + REQUIRE(msgSet.messageMap[0].size == 1); + + REQUIRE(msgSet.pairMap[0].partIndex == 0); + REQUIRE(msgSet.pairMap[0].payloadIndex == 0); +} + +TEST_CASE("MessageSetAddMultiple") +{ + std::vector ptrs; + std::unique_ptr msg(nullptr); + std::unique_ptr msg2(nullptr); + ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(msg2)); + PartRef ref{std::move(msg), std::move(msg2)}; + o2::framework::MessageSet msgSet; + msgSet.add(std::move(ref)); + PartRef ref2{std::move(msg), std::move(msg2)}; + msgSet.add(std::move(ref2)); + std::vector msgs; + msgs.push_back(std::unique_ptr(nullptr)); + msgs.push_back(std::unique_ptr(nullptr)); + msgs.push_back(std::unique_ptr(nullptr)); + msgSet.add([&msgs](size_t i) { + return std::move(msgs[i]); + }, 3); + + REQUIRE(msgSet.messages.size() == 7); + REQUIRE(msgSet.messageMap.size() == 3); + REQUIRE(msgSet.pairMap.size() == 4); + REQUIRE(msgSet.messageMap[0].position == 0); + REQUIRE(msgSet.messageMap[0].size == 1); + REQUIRE(msgSet.messageMap[1].position == 2); + REQUIRE(msgSet.messageMap[1].size == 1); + REQUIRE(msgSet.messageMap[2].position == 4); + REQUIRE(msgSet.messageMap[2].size == 2); + + REQUIRE(msgSet.pairMap[0].partIndex == 0); + REQUIRE(msgSet.pairMap[0].payloadIndex == 0); + REQUIRE(msgSet.pairMap[1].partIndex == 1); + REQUIRE(msgSet.pairMap[1].payloadIndex == 0); + REQUIRE(msgSet.pairMap[2].partIndex == 2); + REQUIRE(msgSet.pairMap[2].payloadIndex == 0); + REQUIRE(msgSet.pairMap[3].partIndex == 2); + REQUIRE(msgSet.pairMap[3].payloadIndex == 1); +} diff --git a/Framework/Core/test/test_ResourcesMonitoringHelpers.cxx b/Framework/Core/test/test_ResourcesMonitoringHelpers.cxx new file mode 100644 index 0000000000000..e31ccea0f5a35 --- /dev/null +++ b/Framework/Core/test/test_ResourcesMonitoringHelpers.cxx @@ -0,0 +1,272 @@ +// 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 "../src/ResourcesMonitoringHelper.h" +#include "Framework/DeviceMetricsInfo.h" +#include "Framework/DeviceMetricsHelper.h" + +#include +#include +#include +#include + +TEST_CASE("StreamMetrics") +{ + using namespace o2::framework; + std::vector specs{ + DeviceSpec{ + .name = "someDevice", + .id = "someDevice", + .inputChannels = {}, + .outputChannels = {}, + .arguments = {}, + .options = {}, + .services = {}, + .algorithm = AlgorithmSpec{}, + .inputs = {}, + .outputs = {}, + .forwards = {}, + .rank = 0, + .nSlots = 0, + .inputTimesliceId = 0, + .maxInputTimeslices = 0, + .completionPolicy = CompletionPolicy{}, + .dispatchPolicy = DispatchPolicy{}, + .callbacksPolicy = CallbacksPolicy{}, + .sendingPolicy = SendingPolicy{}, + .resourcePolicy = ResourcePolicy{}, + .resource = {}, + .resourceMonitoringInterval = 10, + .labels = {}, + .metadata = {}}, + DeviceSpec{ + .name = "anotherDevice", + .id = "anotherDevice", + .inputChannels = {}, + .outputChannels = {}, + .arguments = {}, + .options = {}, + .services = {}, + .algorithm = AlgorithmSpec{}, + .inputs = {}, + .outputs = {}, + .forwards = {}, + .rank = 0, + .nSlots = 0, + .inputTimesliceId = 0, + .maxInputTimeslices = 0, + .completionPolicy = CompletionPolicy{}, + .dispatchPolicy = DispatchPolicy{}, + .callbacksPolicy = CallbacksPolicy{}, + .sendingPolicy = SendingPolicy{}, + .resourcePolicy = ResourcePolicy{}, + .resource = {}, + .resourceMonitoringInterval = 10, + .labels = {}, + .metadata = {}}, + + }; + + // This is the device metrics + std::vector metrics; + metrics.resize(2); + { + DeviceMetricsInfo& info = metrics[0]; + auto bkey = DeviceMetricsHelper::createNumericMetric(info, "bkey"); + REQUIRE(info.metricLabels.size() == 1); + REQUIRE(info.metricPrefixes.size() == 1); + auto akey = DeviceMetricsHelper::createNumericMetric(info, "akey"); + REQUIRE(info.metricLabels.size() == 2); + REQUIRE(info.metricPrefixes.size() == 2); + auto ckey = DeviceMetricsHelper::createNumericMetric(info, "ckey"); + REQUIRE(info.metricLabels.size() == 3); + REQUIRE(info.metricPrefixes.size() == 3); + REQUIRE(DeviceMetricsHelper::metricIdxByName("akey", info) == 1); + REQUIRE(DeviceMetricsHelper::metricIdxByName("bkey", info) == 0); + REQUIRE(DeviceMetricsHelper::metricIdxByName("ckey", info) == 2); + REQUIRE(info.changed.size() == 3); + REQUIRE(info.changed.at(0) == false); + size_t t = 1000; + ckey(info, 0, t++); + ckey(info, 1, t++); + ckey(info, 2, t++); + ckey(info, 3, t++); + ckey(info, 4, t++); + ckey(info, 5, t++); + } + // Metrics for the second device + { + DeviceMetricsInfo& info = metrics[1]; + auto bkey = DeviceMetricsHelper::createNumericMetric(info, "bkey"); + REQUIRE(info.metricLabels.size() == 1); + REQUIRE(info.metricPrefixes.size() == 1); + auto akey = DeviceMetricsHelper::createNumericMetric(info, "akey"); + REQUIRE(info.metricLabels.size() == 2); + REQUIRE(info.metricPrefixes.size() == 2); + auto ckey = DeviceMetricsHelper::createNumericMetric(info, "ckey"); + REQUIRE(info.metricLabels.size() == 3); + REQUIRE(info.metricPrefixes.size() == 3); + REQUIRE(DeviceMetricsHelper::metricIdxByName("akey", info) == 1); + REQUIRE(DeviceMetricsHelper::metricIdxByName("bkey", info) == 0); + REQUIRE(DeviceMetricsHelper::metricIdxByName("ckey", info) == 2); + REQUIRE(info.changed.size() == 3); + REQUIRE(info.changed.at(0) == false); + size_t t = 1000; + bkey(info, 0, t++); + bkey(info, 1, t++); + bkey(info, 2, t++); + bkey(info, 3, t++); + bkey(info, 4, t++); + bkey(info, 5, t++); + } + + // This is the driver metrics + DeviceMetricsInfo driverMetrics; + auto dbkey = DeviceMetricsHelper::createNumericMetric(driverMetrics, "bkey"); + REQUIRE(driverMetrics.metricLabels.size() == 1); + REQUIRE(driverMetrics.metricPrefixes.size() == 1); + auto dakey = DeviceMetricsHelper::createNumericMetric(driverMetrics, "akey"); + REQUIRE(driverMetrics.metricLabels.size() == 2); + REQUIRE(driverMetrics.metricPrefixes.size() == 2); + auto dckey = DeviceMetricsHelper::createNumericMetric(driverMetrics, "ckey"); + REQUIRE(driverMetrics.metricLabels.size() == 3); + REQUIRE(driverMetrics.metricPrefixes.size() == 3); + REQUIRE(DeviceMetricsHelper::metricIdxByName("akey", driverMetrics) == 1); + REQUIRE(DeviceMetricsHelper::metricIdxByName("bkey", driverMetrics) == 0); + REQUIRE(DeviceMetricsHelper::metricIdxByName("ckey", driverMetrics) == 2); + REQUIRE(driverMetrics.changed.size() == 3); + REQUIRE(driverMetrics.changed.at(0) == false); + size_t t = 2000; + dbkey(driverMetrics, 0, t++); + dbkey(driverMetrics, 1, t++); + dbkey(driverMetrics, 2, t++); + dbkey(driverMetrics, 3, t++); + dbkey(driverMetrics, 4, t++); + dbkey(driverMetrics, 5, t++); + + dbkey(driverMetrics, 0, t++); + dbkey(driverMetrics, 1, t++); + + dckey(driverMetrics, 0, t++); + + std::stringstream streamer; + std::vector performanceMetrics{ + std::regex("bkey"), + std::regex("ckey"), + }; + + ResourcesMonitoringHelper::dumpMetricsToJSON(metrics, + driverMetrics, specs, performanceMetrics, + streamer); + std::string streamed = streamer.str(); + std::string expected = R"JSON({ + "someDevice": { + "ckey": [ + { + "timestamp": "1000", + "value": "0" + }, + { + "timestamp": "1001", + "value": "1" + }, + { + "timestamp": "1002", + "value": "2" + }, + { + "timestamp": "1003", + "value": "3" + }, + { + "timestamp": "1004", + "value": "4" + }, + { + "timestamp": "1005", + "value": "5" + } + ] + }, + "anotherDevice": { + "bkey": [ + { + "timestamp": "1000", + "value": "0" + }, + { + "timestamp": "1001", + "value": "1" + }, + { + "timestamp": "1002", + "value": "2" + }, + { + "timestamp": "1003", + "value": "3" + }, + { + "timestamp": "1004", + "value": "4" + }, + { + "timestamp": "1005", + "value": "5" + } + ] + }, + "driver": { + "bkey": [ + { + "timestamp": "2000", + "value": "0" + }, + { + "timestamp": "2001", + "value": "1" + }, + { + "timestamp": "2002", + "value": "2" + }, + { + "timestamp": "2003", + "value": "3" + }, + { + "timestamp": "2004", + "value": "4" + }, + { + "timestamp": "2005", + "value": "5" + }, + { + "timestamp": "2006", + "value": "0" + }, + { + "timestamp": "2007", + "value": "1" + } + ], + "ckey": [ + { + "timestamp": "2008", + "value": "0" + } + ] + } +} +)JSON"; + REQUIRE(std::regex_replace(streamed, std::regex(R"(\s+)"), "") == std::regex_replace(expected, std::regex(R"(\s+)"), "")); +} diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx new file mode 100644 index 0000000000000..cd5a359db0446 --- /dev/null +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -0,0 +1,65 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "Framework/AnalysisTask.h" +#include + +using namespace o2::framework; + +TEST_CASE("TypeIdHelpers_BasicConversion") +{ + // Basic CamelCase to snake-case conversion +// REQUIRE((type_to_task_name(std::string_view("SimpleTask")) == "simple-task")); +// REQUIRE((type_to_task_name(std::string_view("MyTask")) == "my-task")); +// REQUIRE((type_to_task_name(std::string_view("Task")) == "task")); +} + +TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") +{ +// Test ALICE detector abbreviations +// REQUIRE(type_to_task_name(std::string_view("ITSQA")) == "its-qa"); +// REQUIRE(type_to_task_name(std::string_view("TPCQCTask")) == "tpc-qc-task"); +// REQUIRE(type_to_task_name(std::string_view("EMCALQATask")) == "emcal-qa-task"); +// REQUIRE(type_to_task_name(std::string_view("HMPIDTask")) == "hmpid-task"); +// REQUIRE(type_to_task_name(std::string_view("ITSTPCTask")) == "its-tpc-task"); +// REQUIRE(type_to_task_name(std::string_view("QCFV0Task")) == "qc-fv0-task"); +} + +//TEST_CASE("TypeIdHelpers_QualityControlAbbreviations") +//{ +// // Test quality control abbreviations +// REQUIRE(type_to_task_name(std::string_view("QATask")) == "qa-task"); +// REQUIRE(type_to_task_name(std::string_view("QCTask")) == "qc-task"); +// REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); +//} + +//TEST_CASE("TypeIdHelpers_ComplexNames") +//{ +// Test complex combinations +// REQUIRE(type_to_task_name(std::string_view("ITSQAAnalysisTask")) == "its-qa-analysis-task"); +// REQUIRE(type_to_task_name(std::string_view("TPCEMCQCTask")) == "tpc-emc-qc-task"); +// REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); +//} + +//TEST_CASE("TypeIdHelpers_EdgeCases") +//{ +// // Single character +// REQUIRE(type_to_task_name(std::string_view("A")) == "a"); +// +// // All uppercase. BC is Bunch Crossing! +// // +// REQUIRE(type_to_task_name(std::string_view("ABC")) == "a-bc"); +// REQUIRE(type_to_task_name(std::string_view("BC")) == "bc"); +// +// // Mixed with numbers (numbers are not uppercase, so no hyphens before them) +// REQUIRE(type_to_task_name(std::string_view("Task123")) == "task123"); +//} diff --git a/Framework/Foundation/include/Framework/CheckTypes.h b/Framework/Foundation/include/Framework/CheckTypes.h index f0c74c54f9790..95e99a67ac0cf 100644 --- a/Framework/Foundation/include/Framework/CheckTypes.h +++ b/Framework/Foundation/include/Framework/CheckTypes.h @@ -18,13 +18,15 @@ namespace o2::framework { -/// Helper to understand if a given type is complete (declared fully) or not (forward declared). -/// See also: https://devblogs.microsoft.com/oldnewthing/20190710-00/?p=102678 -template -constexpr bool is_type_complete_v = false; +template +concept TypeComplete = requires(T) { + { + sizeof(T) + }; +}; template -constexpr bool is_type_complete_v> = true; +constexpr bool is_type_complete_v = TypeComplete; /// Helper which will invoke @a onDefined if the type T is actually available /// or @a onUndefined if the type T is a forward declaration. @@ -39,31 +41,12 @@ void call_if_defined_full(TDefined&& onDefined, TUndefined&& onUndefined) } } -/// Helper which will invoke @a onDefined if the type T is actually available -/// or @a onUndefined if the type T is a forward declaration. -/// Can be used to check for existence or not of a given type. -template -T call_if_defined_full_forward(TDefined&& onDefined, TUndefined&& onUndefined) -{ - if constexpr (is_type_complete_v) { - return std::move(onDefined(static_cast(nullptr))); - } else { - return onUndefined(); - } -} - template void call_if_defined(TDefined&& onDefined) { call_if_defined_full(onDefined, []() -> void {}); } -template -T call_if_defined_forward(TDefined&& onDefined) -{ - return std::move(call_if_defined_full_forward(onDefined, []() -> T&& { O2_BUILTIN_UNREACHABLE(); })); -} - } // namespace o2::framework #endif // O2_FRAMEWORK_CHECKTYPES_H_ diff --git a/Framework/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 5748329f6a50d..1c7aa62260bd3 100644 --- a/Framework/Foundation/include/Framework/StructToTuple.h +++ b/Framework/Foundation/include/Framework/StructToTuple.h @@ -174,9 +174,9 @@ consteval int nested_brace_constructible_size() return brace_constructible_size() - nesting; } -template () / 10> +template () / 10, typename L> requires(D == 9) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -194,9 +194,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 8) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -214,9 +214,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 7) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -234,9 +234,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 6) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -254,9 +254,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 5) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -274,9 +274,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 4) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -294,9 +294,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 3) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -314,9 +314,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 2) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -334,9 +334,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 1) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -354,9 +354,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 0) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -373,6 +373,12 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } +template +constexpr auto homogeneous_apply_refs_sized(L l, T&& object) +{ + return homogeneous_apply_refs(l, object); +} + } // namespace o2::framework #endif // O2_FRAMEWORK_STRUCTTOTUPLE_H_ diff --git a/Framework/Foundation/include/Framework/TypeIdHelpers.h b/Framework/Foundation/include/Framework/TypeIdHelpers.h index 5eaac2151b909..1dc2464b40ec8 100644 --- a/Framework/Foundation/include/Framework/TypeIdHelpers.h +++ b/Framework/Foundation/include/Framework/TypeIdHelpers.h @@ -13,7 +13,6 @@ #define O2_FRAMEWORK_TYPEIDHELPERS_H_ #include -#include #if __cplusplus >= 202002L #include #endif @@ -82,22 +81,6 @@ struct TypeIdHelpers { } }; -/// Convert a CamelCase task struct name to snake-case task name -inline static std::string type_to_task_name(std::string_view& camelCase) -{ - std::ostringstream str; - str << static_cast(std::tolower(camelCase[0])); - - for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) { - if (std::isupper(*it) && *(it - 1) != '-') { - str << "-"; - } - str << static_cast(std::tolower(*it)); - } - - return str.str(); -} - } // namespace o2::framework #endif // O2_FRAMEWORK_TYPEIDHELPERS_H_ diff --git a/Framework/Foundation/src/RuntimeError.cxx b/Framework/Foundation/src/RuntimeError.cxx index 6f31fb40b86f5..7d31389a9641c 100644 --- a/Framework/Foundation/src/RuntimeError.cxx +++ b/Framework/Foundation/src/RuntimeError.cxx @@ -60,8 +60,11 @@ RuntimeError& error_from_ref(RuntimeErrorRef ref) RuntimeErrorRef runtime_error_f(const char* format, ...) { int i = 0; - bool expected = false; - while (gErrorBooking[i].compare_exchange_strong(expected, true) == false) { + while (true) { + bool expected = false; + if (gErrorBooking[i].compare_exchange_strong(expected, true) == true) { + break; + } ++i; if (i >= RuntimeError::MAX_RUNTIME_ERRORS) { throw std::runtime_error("Too many o2::framework::runtime_error thrown without proper cleanup."); @@ -78,11 +81,18 @@ RuntimeErrorRef runtime_error_f(const char* format, ...) RuntimeErrorRef runtime_error(const char* s) { int i = 0; - bool expected = false; - while (gErrorBooking[i].compare_exchange_strong(expected, true) == false) { + while (true) { + bool expected = false; + if (gErrorBooking[i].compare_exchange_strong(expected, true) == true) { + break; + } ++i; + if (i >= RuntimeError::MAX_RUNTIME_ERRORS) { + throw std::runtime_error("Too many o2::framework::runtime_error thrown without proper cleanup."); + } } - strncpy(gError[i].what, s, RuntimeError::MAX_RUNTIME_ERROR_SIZE); + strncpy(gError[i].what, s, RuntimeError::MAX_RUNTIME_ERROR_SIZE - 1); + gError[i].what[RuntimeError::MAX_RUNTIME_ERROR_SIZE - 1] = 0; gError[i].maxBacktrace = canDumpBacktrace() ? backtrace(gError[i].backtrace, BacktraceHelpers::MAX_BACKTRACE_SIZE) : 0; return RuntimeErrorRef{i}; } diff --git a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx index 1d3b4f24ea34c..86558d22b973d 100644 --- a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx @@ -43,7 +43,7 @@ struct HeatMapHelper { { float padding = 1; // add slider to scroll between the grid display windows - size_t nw = getNumRecords() / WND; + size_t nw = getNumRecords() < WND ? 1 : getNumRecords() / WND; ImGui::PushItemWidth(sizeHint.x); ImGui::SliderInt("##window", &v, 1, nw, "wnd: %d", ImGuiSliderFlags_AlwaysClamp); ImVec2 sliderMin = ImGui::GetItemRectMin(); @@ -51,7 +51,7 @@ struct HeatMapHelper { constexpr float MAX_BOX_X_SIZE = 16.f; constexpr float MAX_BOX_Y_SIZE = 16.f; - ImVec2 size = ImVec2(sizeHint.x, std::min(sizeHint.y, MAX_BOX_Y_SIZE * getNumItems(0) + 2)); + ImVec2 size = ImVec2(sizeHint.x, std::min(sizeHint.y, MAX_BOX_Y_SIZE * getNumInputs() + 2)); ImU32 BORDER_COLOR = ImColor(200, 200, 200, 255); ImU32 BACKGROUND_COLOR = ImColor(20, 20, 20, 255); ImU32 BORDER_COLOR_A = ImColor(200, 200, 200, 0); @@ -75,19 +75,22 @@ struct HeatMapHelper { const static auto colorE = ImColor(ImVec4{0, 0, 0, 0}); drawList->PrimReserve(nw * 6, nw * 4); - for (size_t iw = 0; iw < nw; ++iw) { - ImVec2 xOffset{iw * xsz + 2 * padding, 0}; + for (size_t iw = 1; iw <= nw; ++iw) { + ImVec2 xOffset{(iw - 1) * xsz + 2 * padding, 0}; ImVec2 xSize{xsz - 2 * padding, 0}; ImVec2 yOffset{0, 2 * padding}; - ImVec2 ySize{0, 16 - 4 * padding}; - bool active = 0; - for (size_t ir = iw; ir < ((iw + WND > getNumRecords()) ? getNumRecords() : iw + WND); ++ir) { - for (size_t i = 0; i < getNumItems(ir); ++i) { - active = getValue(*getItem(ir, i)) > 0; + ImVec2 ySize{0, MAX_BOX_Y_SIZE - 4 * padding}; + bool active = false; + for (size_t ir = (iw - 1) * WND; ir < ((iw * WND > getNumRecords()) ? getNumRecords() : iw * WND); ++ir) { + for (size_t i = 0; i < getNumItems(getRecord(ir)); ++i) { + active = getValue(*getItem(getRecord(ir), i)) > 0; if (active) { break; } } + if (active) { + break; + } } drawList->PrimRect( xOffset + yOffset + winPos, @@ -96,47 +99,46 @@ struct HeatMapHelper { } // display the grid - size_t recordsWindow = v * WND; auto boxSizeX = std::min(size.x / WND, MAX_BOX_X_SIZE); - auto numInputs = getNumInputs(); + auto boxSizeY = std::min(size.y / getNumInputs(), MAX_BOX_Y_SIZE); + winPos = ImGui::GetCursorScreenPos() + ImVec2{0, 7}; - ImGui::InvisibleButton("sensible area", ImVec2(size.x, size.y)); + ImGui::InvisibleButton("sensitive area", ImVec2(size.x, size.y)); if (ImGui::IsItemHovered()) { auto pos = ImGui::GetMousePos() - winPos; - auto slot = (v - 1) * WND + std::lround(std::trunc(pos.x / size.x * WND)); - auto row = std::lround(std::trunc(pos.y / size.y * numInputs)); + auto slot = (v - 1) * WND + std::lround(std::trunc(pos.x / boxSizeX)); + auto row = std::lround(std::trunc(pos.y / boxSizeY)); describeCell(row, slot); } + // background drawList->AddRectFilled( ImVec2(0., 0.) + winPos, ImVec2{size.x, size.y} + winPos, BACKGROUND_COLOR); + // border drawList->AddRect( ImVec2(0. - 1, -1) + winPos, ImVec2{size.x + 1, size.y - 1} + winPos, BORDER_COLOR); - size_t totalRects = 0; - for (size_t ri = (v - 1) * WND; ri < recordsWindow; ri++) { - auto record = getRecord(ri); - totalRects += getNumItems(record); - } - - drawList->PrimReserve(totalRects * 6, totalRects * 4); - for (size_t ri = (v - 1) * WND; ri < recordsWindow; ri++) { + // heatmap + size_t totalPrims = WND * getNumInputs(); + drawList->PrimReserve(totalPrims * 6, totalPrims * 4); + for (size_t ri = (v - 1) * WND; ri < (((size_t)(v)*WND > getNumRecords()) ? getNumRecords() : v * WND); ++ri) { auto record = getRecord(ri); - ImVec2 xOffset{((ri - (v - 1) * WND) * boxSizeX) + padding, 0}; + ImVec2 xOffset{((float)(ri - (v - 1) * WND) * boxSizeX) + padding, 0}; ImVec2 xSize{boxSizeX - 2 * padding, 0}; - auto me = getNumItems(record); - auto boxSizeY = std::min(size.y / me, MAX_BOX_Y_SIZE); - for (size_t mi = 0; mi < me; mi++) { - ImVec2 yOffSet{0, (mi * boxSizeY) + padding}; + + for (auto mi = 0U; mi < getNumItems(record); mi++) { + ImVec2 yOffSet{0, ((float)mi * boxSizeY) + padding}; ImVec2 ySize{0, boxSizeY - 2 * padding}; + ImVec2 A = xOffset + yOffSet + winPos; + ImVec2 B = xOffset + xSize + yOffSet + ySize + winPos; + drawList->PrimRect( - xOffset + yOffSet + winPos, - xOffset + xSize + yOffSet + ySize + winPos, + A, B, getColor(getValue(*getItem(record, mi)))); } } diff --git a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx index b8c9cc50f0770..aa546b8a9ab49 100644 --- a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx @@ -329,6 +329,10 @@ void displayDeviceInspector(DeviceSpec const& spec, control.controller->write("/shm-offer 1000", strlen("/shm-offer 1000")); } + if (ImGui::Button("Offer timeslices")) { + control.controller->write("/timeslice-offer 1", strlen("/timeslice-offer 1")); + } + if (control.requestedState > info.providedState) { ImGui::Text(ICON_FA_CLOCK_O); } else { diff --git a/Framework/TestWorkflows/CMakeLists.txt b/Framework/TestWorkflows/CMakeLists.txt index f5d18183c3705..d2b98419043bf 100644 --- a/Framework/TestWorkflows/CMakeLists.txt +++ b/Framework/TestWorkflows/CMakeLists.txt @@ -46,6 +46,10 @@ o2_add_dpl_workflow(analysis-ccdb PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF COMPONENT_NAME TestWorkflows) +o2_add_dpl_workflow(analysis-emb + SOURCES src/o2TestMultisource.cxx + COMPONENT_NAME TestWorkflows) + o2_add_dpl_workflow(two-timers SOURCES src/o2TwoTimers.cxx COMPONENT_NAME TestWorkflows) diff --git a/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx b/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx index 841f4a8f2b9bd..bdc08ad45ea24 100644 --- a/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx +++ b/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx @@ -9,7 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "Framework/ConcreteDataMatcher.h" #include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" #include #include @@ -29,13 +34,16 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"3-layer-pipelining", VariantType::Int, 1, {timeHelp}}); } +void customize(std::vector& policies) +{ + policies = { + CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe("merger-policy", [](auto const&) -> bool { return true; })}; +} + #include "Framework/runDataProcessing.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ParallelContext.h" -#include "Framework/ControlService.h" - -#include "Framework/Logger.h" #include @@ -43,22 +51,24 @@ using DataHeader = o2::header::DataHeader; DataProcessorSpec templateProcessor() { - return DataProcessorSpec{"some-processor", { - InputSpec{"x", "TST", "A", 0, Lifetime::Timeframe}, - }, - { + return DataProcessorSpec{.name = "some-processor", + .inputs = { + InputSpec{"x", "TST", "A", 0, Lifetime::Timeframe}, + }, + .outputs = { OutputSpec{"TST", "P", 0, Lifetime::Timeframe}, }, // The producer is stateful, we use a static for the state in this // particular case, but a Singleton or a captured new object would // work as well. - AlgorithmSpec{[](InitContext& setup) { + .algorithm = AlgorithmSpec{[](InitContext& setup) { srand(setup.services().get().index1D()); return [](ProcessingContext& ctx) { // Create a single output. size_t index = ctx.services().get().index1D(); - auto& aData = ctx.outputs().make( + auto& i = ctx.outputs().make( Output{"TST", "P", static_cast(index)}, 1); + i[0] = index; std::this_thread::sleep_for(std::chrono::seconds(rand() % 5)); }; }}}; @@ -86,34 +96,43 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) outputSpecs.emplace_back("TST", "A", ssi); } - workflow.push_back(DataProcessorSpec{"reader", {}, outputSpecs, AlgorithmSpec{[jobs](InitContext& initCtx) { - return [jobs](ProcessingContext& ctx) { - for (size_t ji = 0; ji < jobs; ++ji) { - ctx.outputs().make(Output{"TST", "A", static_cast(ji)}, - 1); - } - }; - }}}); + workflow.push_back(DataProcessorSpec{ + .name = "reader", + .outputs = outputSpecs, + .algorithm = AlgorithmSpec{[jobs](InitContext& initCtx) { + return [jobs](ProcessingContext& ctx) { + static int count = 0; + for (size_t ji = 0; ji < jobs; ++ji) { + int& i = ctx.outputs().make(Output{"TST", "A", static_cast(ji)}); + i = count * 100 + ji; + } + count++; + }; + }}}); workflow.push_back(timePipeline(DataProcessorSpec{ - "merger", - mergeInputs(InputSpec{"x", "TST", "P"}, - jobs, - [](InputSpec& input, size_t index) { - DataSpecUtils::updateMatchingSubspec(input, index); - }), - {OutputSpec{{"out"}, "TST", "M"}}, - AlgorithmSpec{[](InitContext& setup) { + .name = "merger", + .inputs = {InputSpec{"all", ConcreteDataTypeMatcher{"TST", "P"}}}, + .outputs = {OutputSpec{{"out"}, "TST", "M"}}, + .algorithm = AlgorithmSpec{[](InitContext& setup) { return [](ProcessingContext& ctx) { + LOGP(info, "Run"); + for (const auto& input : o2::framework::InputRecordWalker(ctx.inputs())) { + if (input.header == nullptr) { + LOGP(error, "Missing header"); + continue; + } + int record = *(int*)input.payload; + LOGP(info, "Record {}", record); + } ctx.outputs().make(OutputRef("out", 0), 1); }; }}}, stages)); workflow.push_back(DataProcessorSpec{ - "writer", - {InputSpec{"x", "TST", "M"}}, - {}, - AlgorithmSpec{[](InitContext& setup) { + .name = "writer", + .inputs = {InputSpec{"x", "TST", "M"}}, + .algorithm = AlgorithmSpec{[](InitContext& setup) { return [](ProcessingContext& ctx) { }; }}}); diff --git a/Framework/TestWorkflows/src/o2TestHistograms.cxx b/Framework/TestWorkflows/src/o2TestHistograms.cxx index ae3610ca01e67..9c2cba35b9156 100644 --- a/Framework/TestWorkflows/src/o2TestHistograms.cxx +++ b/Framework/TestWorkflows/src/o2TestHistograms.cxx @@ -16,8 +16,6 @@ #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include -#include -#include using namespace o2; using namespace o2::framework; @@ -53,6 +51,18 @@ struct EtaAndClsHistogramsSimple { } // }; + HistogramRegistry registry2{ + "registry2", + { + {"a/foo/b/eta", "#Eta", {HistType::kTH1F, {{100, -2.0, 2.0}}}}, // + {"fii/c/hpt", "p_{T}", {HistType::kTH1D, {{1002, -0.01, 50.1}}}}, // + {"a/foobar/phi", "#Phi", {HistType::kTH1D, {{102, 0, 2 * M_PI}}}}, // + {"fifi/ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + }, + OutputObjHandlingPolicy::AnalysisObject, + false, + true}; + void init(InitContext&) { if (!trackFilterString->empty()) { @@ -60,7 +70,7 @@ struct EtaAndClsHistogramsSimple { } } - void process(soa::Filtered const& tracks, aod::FT0s const&, aod::StoredTracksFrom> const& ortherTracks) + void process(soa::Filtered const& tracks, aod::FT0s const&) { LOGP(info, "Invoking the simple one"); for (auto& track : tracks) { @@ -98,7 +108,7 @@ struct EtaAndClsHistogramsIUSimple { } } - void process(soa::Filtered const& tracks, aod::FT0s const&, aod::TracksIUFrom> const& otherTracks) + void process(soa::Filtered const& tracks, aod::FT0s const&) { LOGP(info, "Invoking the simple one IU"); for (auto& track : tracks) { diff --git a/Framework/TestWorkflows/src/o2TestMultisource.cxx b/Framework/TestWorkflows/src/o2TestMultisource.cxx new file mode 100644 index 0000000000000..00bd9ba5093bd --- /dev/null +++ b/Framework/TestWorkflows/src/o2TestMultisource.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \brief Tests that the same tables from different origins are routed correctly. +/// Requires two input files, .root and _EMB.root, that contain +/// same number of DFs with the same names. +/// \author +/// \since + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +O2ORIGIN("EMB"); +template +using BCsFrom = BCs_001From; +using TracksPlus = soa::Join; +template +using TracksPlusFrom = soa::Join, StoredTracksExtra_002From>; +} // namespace o2::aod + +struct TestEmbeddingSubscription { + void process(aod::BCs const& bcs, aod::BCsFrom> const& bcse, + aod::TracksPlus const& tracks, aod::TracksPlusFrom> const& trackse) + { + LOGP(info, "BCs from run {} and {}", bcs.begin().runNumber(), bcse.begin().runNumber()); + LOGP(info, "Joined tracks: {} and {}", tracks.size(), trackse.size()); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return {adaptAnalysisTask(cfgc)}; +} diff --git a/Framework/TestWorkflows/src/o2_sim_tpc.cxx b/Framework/TestWorkflows/src/o2_sim_tpc.cxx deleted file mode 100644 index 4587c0fcb831f..0000000000000 --- a/Framework/TestWorkflows/src/o2_sim_tpc.cxx +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#include "Framework/DataRefUtils.h" -#include "Framework/WorkflowSpec.h" -#include -#include "Framework/RootFileService.h" -#include "Framework/AlgorithmSpec.h" -#include "Framework/ConfigParamRegistry.h" - -#include "Framework/Logger.h" - -#include "FairRunSim.h" -#include -#include "FairRuntimeDb.h" -#include "FairPrimaryGenerator.h" -#include "FairBoxGenerator.h" -#include "FairParRootFileIo.h" - -#include "DetectorsPassive/Cave.h" -#include "Field/MagneticField.h" - -#include "DetectorsPassive/Cave.h" -#include "Generators/GeneratorFromFile.h" -#include "TPCSimulation/Detector.h" -#include "Framework/OutputSpec.h" -#include - -using namespace o2::framework; - -#define BOX_GENERATOR 1 - -namespace o2 -{ -namespace workflows -{ - -DataProcessorSpec sim_tpc() -{ - return { - "sim_tpc", - Inputs{}, - Outputs{OutputSpec{"TPC", "GEN"}}, - AlgorithmSpec{ - [](InitContext& setup) { - int nEvents = setup.options().get("nEvents"); - auto mcEngine = setup.options().get("mcEngine"); - - // FIXME: this should probably be part of some generic - // FairRunInitSpec - TString dir = getenv("VMCWORKDIR"); - TString geom_dir = dir + "/Detectors/Geometry/"; - gSystem->Setenv("GEOMPATH", geom_dir.Data()); - - TString tut_configdir = dir + "/Detectors/gconfig"; - gSystem->Setenv("CONFIG_DIR", tut_configdir.Data()); - - // Requiring a file is something which requires IO, and it's therefore - // delegated to the framework - auto& rfm = setup.services().get(); - // FIXME: We should propably have a service for FairRunSim, rather than - // for the root files themselves... - // Output file name - auto outFile = rfm.format("AliceO2_%s.tpc.mc_%i_event.root", mcEngine.c_str(), nEvents); - - // Parameter file name - auto parFile = rfm.format("AliceO2_%s.tpc.mc_%i_event.root", mcEngine.c_str(), nEvents); - - // Create simulation run - FairRunSim* run = new FairRunSim(); - - run->SetName(mcEngine.c_str()); - run->SetSink(new FairRootFileSink(outFile.c_str())); // Output file - FairRuntimeDb* rtdb = run->GetRuntimeDb(); - - // Create media - run->SetMaterials("media.geo"); // Materials - - // Create geometry - o2::passive::Cave* cave = new o2::passive::Cave("CAVE"); - cave->SetGeometryFileName("cave.geo"); - run->AddModule(cave); - - o2::field::MagneticField* magField = new o2::field::MagneticField("Maps", "Maps", -1., -1., o2::field::MagFieldParam::k5kG); - run->SetField(magField); - - // ===| Add TPC |============================================================ - o2::tpc::Detector* tpc = new o2::tpc::Detector(kTRUE); - tpc->SetGeoFileName("TPCGeometry.root"); - run->AddModule(tpc); - - // Create PrimaryGenerator - FairPrimaryGenerator* primGen = new FairPrimaryGenerator(); -#ifdef BOX_GENERATOR - FairBoxGenerator* boxGen = new FairBoxGenerator(211, 10); /*protons*/ - - //boxGen->SetThetaRange(0.0, 90.0); - boxGen->SetEtaRange(-0.9, 0.9); - boxGen->SetPRange(0.1, 5); - boxGen->SetPhiRange(0., 360.); - boxGen->SetDebug(kTRUE); - - primGen->AddGenerator(boxGen); -#else - // reading the events from a kinematics file (produced by AliRoot) - auto extGen = new o2::eventgen::GeneratorFromFile(params.get("extKinFile")); - extGen->SetStartEvent(params.get("startEvent")); - primGen->AddGenerator(extGen); -#endif - - run->SetGenerator(primGen); - - // store track trajectories - // run->SetStoreTraj(kTRUE); - - // Initialize simulation run - run->Init(); - - // Runtime database - Bool_t kParameterMerged = kTRUE; - FairParRootFileIo* parOut = new FairParRootFileIo(kParameterMerged); - parOut->open(parFile.c_str()); - rtdb->setOutput(parOut); - rtdb->saveOutput(); - rtdb->print(); - run->Run(nEvents); - - static bool once = true; - - // This is the actual inner loop for the device - return [run, nEvents](ProcessingContext& ctx) { - if (!once) { - run->Run(nEvents); - once = true; - } else { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - // FIXME: After we run we should readback events - // and push them as messages, for the next stage of - // processing. - }; - }}, - Options{ - {"mcEngine", VariantType::String, "TGeant3", {"Engine to use"}}, - {"nEvents", VariantType::Int, 10, {"Events to process"}}, - {"extKinFile", VariantType::String, "Kinematics.root", {"name of kinematics file for event generator from file (when applicable)"}}, - {"startEvent", VariantType::Int, 2, {"Events to skip"}}}}; -}; -} // namespace workflows -} // namespace o2 diff --git a/Framework/TestWorkflows/src/o2_sim_tpc.h b/Framework/TestWorkflows/src/o2_sim_tpc.h deleted file mode 100644 index e567fe89e0b38..0000000000000 --- a/Framework/TestWorkflows/src/o2_sim_tpc.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// 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 - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::DataProcessorSpec sim_tpc(); -} -} // namespace o2 - -#endif // WORKFLOWS_O2_SIM_TPC diff --git a/Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx b/Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx deleted file mode 100644 index d6d3cb1242f7c..0000000000000 --- a/Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "o2_sim_its_ALP3.h" -#include "Framework/Logger.h" -#include -#include - -using namespace o2::framework; -using namespace o2::workflows; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - sim_its_ALP3(), - }; -} diff --git a/Framework/TestWorkflows/src/test_o2TPCSimulation.cxx b/Framework/TestWorkflows/src/test_o2TPCSimulation.cxx deleted file mode 100644 index 403ad8bc7127b..0000000000000 --- a/Framework/TestWorkflows/src/test_o2TPCSimulation.cxx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRef.h" -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "o2_sim_tpc.h" -#include "Framework/Logger.h" - -using namespace o2::framework; -using namespace o2::workflows; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const& specs) -{ - return WorkflowSpec{ - sim_tpc(), - }; -} diff --git a/Framework/Utils/include/DPLUtils/RootTreeWriter.h b/Framework/Utils/include/DPLUtils/RootTreeWriter.h index 0161c67396543..b937a83f5972a 100644 --- a/Framework/Utils/include/DPLUtils/RootTreeWriter.h +++ b/Framework/Utils/include/DPLUtils/RootTreeWriter.h @@ -714,7 +714,7 @@ class RootTreeWriter impl.start = &(data[0]); impl.end = &(data[data.size() - 1]) + 1; // end pointer (beyond last element) impl.cap = impl.end; - std::memcpy(&v, &impl, sizeof(VecBase)); + std::memcpy((void*)&v, (const void*)&impl, sizeof(VecBase)); }; // if the value type is messagable and has a ROOT dictionary, two serialization methods are possible diff --git a/Framework/Utils/test/DPLBroadcasterMerger.cxx b/Framework/Utils/test/DPLBroadcasterMerger.cxx deleted file mode 100644 index bf793275d2f3f..0000000000000 --- a/Framework/Utils/test/DPLBroadcasterMerger.cxx +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#include -#include "DPLBroadcasterMerger.h" -#include "DPLUtils/Utils.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/ControlService.h" -#include "Framework/DataRefUtils.h" -#include "random" -#include "Framework/Logger.h" -#include - -namespace o2f = o2::framework; - -namespace o2::workflows -{ - -o2f::Inputs noInputs{}; -o2f::Outputs noOutputs{}; - -o2f::DataProcessorSpec defineGenerator(o2f::OutputSpec usrOutput) -{ - return {"Generator", // Device name - noInputs, // No inputs for a generator - o2f::Outputs{usrOutput}, // One simple output - - o2f::AlgorithmSpec{[usrOutput](o2f::InitContext&) { - int msgCounter = 0; - auto msgCounter_shptr = std::make_shared(msgCounter); - auto usrOutput_shptr = std::make_shared(getOutput(usrOutput)); - - LOG(info) << ">>>>>>>>>>>>>> Generator initialised"; - - // Processing context in captured from return on InitCallback - return [usrOutput_shptr, msgCounter_shptr](o2f::ProcessingContext& ctx) { - int msgIndex = (*msgCounter_shptr)++; - if (msgIndex > 10) { - ctx.services().get().endOfStream(); - } - LOG(info) << ">>> MSG:" << msgIndex; - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - - LOG(info) << ">>> Preparing MSG:" << msgIndex; - - auto& outputMsg = - ctx.outputs().newChunk(*usrOutput_shptr, (msgIndex + 1) * sizeof(uint32_t) / sizeof(char)); - - LOG(info) << ">>> Preparing1 MSG:" << msgIndex; - - auto payload = reinterpret_cast(outputMsg.data()); - - payload[0] = msgIndex; - - LOG(info) << ">>> Preparing2 MSG:" << msgIndex; - - for (int k = 0; k < msgIndex; ++k) { - payload[k + 1] = (uint32_t)32; - LOG(info) << ">>>>\t" << payload[k + 1]; - } - - return; - }; - }}}; -} - -o2f::DataProcessorSpec definePipeline(std::string devName, o2f::InputSpec usrInput, o2f::OutputSpec usrOutput) -{ - return {devName, // Device name - o2f::Inputs{usrInput}, // No inputs, for the moment - o2f::Outputs{usrOutput}, o2f::AlgorithmSpec{[usrOutput](o2f::InitContext&) { - auto output_sharedptr = std::make_shared(getOutput(usrOutput)); - - // Processing context in captured from return on InitCallback - return [output_sharedptr](o2f::ProcessingContext& ctx) { - auto inputMsg = ctx.inputs().getByPos(0); - auto msgSize = o2::framework::DataRefUtils::getPayloadSize(inputMsg); - - auto& fwdMsg = ctx.outputs().newChunk((*output_sharedptr), msgSize); - std::memcpy(fwdMsg.data(), inputMsg.payload, msgSize); - }; - }}}; -} - -o2f::DataProcessorSpec defineSink(o2f::InputSpec usrInput) -{ - return {"Sink", // Device name - o2f::Inputs{usrInput}, // No inputs, for the moment - noOutputs, - - o2f::AlgorithmSpec{[](o2f::InitContext&) { - // Processing context in captured from return on InitCallback - return [](o2f::ProcessingContext& ctx) { - LOG(info) << "Received message "; - - auto inputMsg = ctx.inputs().getByPos(0); - auto payload = reinterpret_cast(inputMsg.payload); - - LOG(info) << "Received message containing" << payload[0] << "elements"; - - for (int j = 0; j < payload[0]; ++j) { - LOG(info) << payload[j + 1] << "\t"; - } - LOG(info); - }; - }}}; -} - -o2::framework::WorkflowSpec DPLBroadcasterMergerWorkflow() -{ - auto lspec = o2f::WorkflowSpec(); - - // A generator of data - lspec.emplace_back(defineGenerator(o2f::OutputSpec{"TST", "ToBC", 0, o2f::Lifetime::Timeframe})); - - // A two-way broadcaster - lspec.emplace_back(defineBroadcaster("Broadcaster", - o2f::InputSpec{"input", "TST", "ToBC", 0, o2f::Lifetime::Timeframe}, - o2f::Outputs{{"TST", "BCAST0", 0, o2f::Lifetime::Timeframe}, - {"TST", "BCAST1", 0, o2f::Lifetime::Timeframe}})); - - // Two pipeline devices - lspec.emplace_back(definePipeline("pip0", o2f::InputSpec{"bc", "TST", "BCAST0", 0, o2f::Lifetime::Timeframe}, - o2f::OutputSpec{"TST", "PIP0", 0, o2f::Lifetime::Timeframe})); - lspec.emplace_back(definePipeline("pip1", o2f::InputSpec{"bc", "TST", "BCAST1", 0, o2f::Lifetime::Timeframe}, - o2f::OutputSpec{"TST", "PIP1", 0, o2f::Lifetime::Timeframe})); - - // A gatherer - lspec.emplace_back(defineMerger("Merger", o2f::Inputs{{"input1", "TST", "PIP0", 0, o2f::Lifetime::Timeframe}, {"input2", "TST", "PIP1", 0, o2f::Lifetime::Timeframe}}, - o2f::OutputSpec{"TST", "ToSink", 0, o2f::Lifetime::Timeframe})); - - // A sink which dumps messages - lspec.emplace_back(defineSink(o2f::InputSpec{"input", "TST", "ToSink", 0, o2f::Lifetime::Timeframe})); - return std::move(lspec); -} - -} // namespace o2::workflows diff --git a/Framework/Utils/test/DPLBroadcasterMerger.h b/Framework/Utils/test/DPLBroadcasterMerger.h deleted file mode 100644 index 4607d72a702b7..0000000000000 --- a/Framework/Utils/test/DPLBroadcasterMerger.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#ifndef DPLBROADCASTERMERGER_H -#define DPLBROADCASTERMERGER_H - -#include "Framework/WorkflowSpec.h" -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::WorkflowSpec DPLBroadcasterMergerWorkflow(); -} -} // namespace o2 - -#endif // DPLBROADCASTERMERGER_H diff --git a/Framework/Utils/test/DPLOutputTest.h b/Framework/Utils/test/DPLOutputTest.h deleted file mode 100644 index ce776ffff1113..0000000000000 --- a/Framework/Utils/test/DPLOutputTest.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#ifndef DPLOUTPUTTEST_H -#define DPLOUTPUTTEST_H - -#include "Framework/WorkflowSpec.h" -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::WorkflowSpec DPLOutputTest(); -} -} // namespace o2 - -#endif // DPLOUTPUTTEST_H diff --git a/Framework/Utils/test/test_DPLBroadcasterMerger.cxx b/Framework/Utils/test/test_DPLBroadcasterMerger.cxx deleted file mode 100644 index 6ff554e75f462..0000000000000 --- a/Framework/Utils/test/test_DPLBroadcasterMerger.cxx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRef.h" -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "DPLBroadcasterMerger.h" -#include "Framework/Logger.h" - -using namespace o2::framework; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return o2::workflows::DPLBroadcasterMergerWorkflow(); -} diff --git a/Framework/Utils/test/test_DPLOutputTest.cxx b/Framework/Utils/test/test_DPLOutputTest.cxx deleted file mode 100644 index e49bea3074dd1..0000000000000 --- a/Framework/Utils/test/test_DPLOutputTest.cxx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRef.h" -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "DPLOutputTest.h" -#include "Framework/Logger.h" - -using namespace o2::framework; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return o2::workflows::DPLOutputTest(); -} diff --git a/GPU/Common/CMakeLists.txt b/GPU/Common/CMakeLists.txt index b1a4b2107019c..8f7a7c2e169ed 100644 --- a/GPU/Common/CMakeLists.txt +++ b/GPU/Common/CMakeLists.txt @@ -52,16 +52,16 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") COMPONENT_NAME GPU LABELS gpu) endif() - if (HIP_ENABLED) - o2_add_test(SMatrixImpHIP NAME test_SMatrixImpHIP - SOURCES test/testSMatrixImp.cu - HIPIFIED test - PUBLIC_LINK_LIBRARIES O2::${MODULE} - O2::MathUtils - ROOT::Core - COMPONENT_NAME GPU - LABELS gpu) - endif() +# if (HIP_ENABLED) +# o2_add_test(SMatrixImpHIP NAME test_SMatrixImpHIP +# SOURCES test/testSMatrixImp.cu +# HIPIFIED test +# PUBLIC_LINK_LIBRARIES O2::${MODULE} +# O2::MathUtils +# ROOT::Core +# COMPONENT_NAME GPU +# LABELS gpu) +# endif() endif() install(FILES ${HDRS_INSTALL} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/GPU) diff --git a/GPU/Common/GPUCommonLogger.h b/GPU/Common/GPUCommonLogger.h index 0b6b5ae401244..a70710e9ae744 100644 --- a/GPU/Common/GPUCommonLogger.h +++ b/GPU/Common/GPUCommonLogger.h @@ -45,7 +45,7 @@ struct DummyLogger { #define LOGP(...) // #define LOGP(...) static_assert(false, "LOGP(...) unsupported in GPU code"); -#elif defined(GPUCA_STANDALONE) +#elif defined(GPUCA_STANDALONE) || defined(GPUCA_GPUCODE_COMPILEKERNELS) || defined(GPUCA_COMPILEKERNELS) #include #include #define LOG(type) std::cout diff --git a/GPU/GPUTracking/Base/GPUConstantMem.h b/GPU/GPUTracking/Base/GPUConstantMem.h index c496151c3dfd0..94ccfa7fa6db9 100644 --- a/GPU/GPUTracking/Base/GPUConstantMem.h +++ b/GPU/GPUTracking/Base/GPUConstantMem.h @@ -17,7 +17,7 @@ #include "GPUTPCTracker.h" #include "GPUParam.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUErrors.h" #include "GPUTPCGMMerger.h" @@ -57,7 +57,6 @@ struct GPUConstantMem { #ifdef GPUCA_HAS_ONNX GPUTPCNNClusterizer tpcNNClusterer[GPUCA_NSECTORS]; #endif - template GPUd() auto& getTRDTracker(); }; diff --git a/GPU/GPUTracking/Base/GPUGeneralKernels.h b/GPU/GPUTracking/Base/GPUGeneralKernels.h index eb816c91f5909..871cc21ee2bfa 100644 --- a/GPU/GPUTracking/Base/GPUGeneralKernels.h +++ b/GPU/GPUTracking/Base/GPUGeneralKernels.h @@ -16,7 +16,8 @@ #define GPUGENERALKERNELS_H #include "GPUDef.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" +#include "GPUDataTypesConfig.h" #if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_COMPILEKERNELS) && !defined(GPUCA_GPUCODE_HOSTONLY) #if defined(__CUDACC__) @@ -79,7 +80,7 @@ class GPUKernelTemplate }; typedef GPUconstantref() GPUConstantMem processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::NoRecoStep; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::NoRecoStep; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return &processors; @@ -94,7 +95,7 @@ class GPUKernelTemplate class GPUMemClean16 : public GPUKernelTemplate { public: - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::NoRecoStep; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::NoRecoStep; } template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors, GPUglobalref() void* ptr, uint64_t size); }; @@ -103,7 +104,7 @@ class GPUMemClean16 : public GPUKernelTemplate class GPUitoa : public GPUKernelTemplate { public: - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::NoRecoStep; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::NoRecoStep; } template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors, GPUglobalref() int32_t* ptr, uint64_t size); }; diff --git a/GPU/GPUTracking/Base/GPUMemoryResource.cxx b/GPU/GPUTracking/Base/GPUMemoryResource.cxx index b22267859345a..aaf31837d04e3 100644 --- a/GPU/GPUTracking/Base/GPUMemoryResource.cxx +++ b/GPU/GPUTracking/Base/GPUMemoryResource.cxx @@ -13,4 +13,15 @@ /// \author David Rohr #include "GPUMemoryResource.h" +#include "GPUProcessor.h" using namespace o2::gpu; + +void* GPUMemoryResource::SetPointers(void* ptr) const +{ + return (mProcessor->*mSetPointers)(ptr); +} + +void* GPUMemoryResource::SetDevicePointers(void* ptr) const +{ + return (mProcessor->mLinkedProcessor->*mSetPointers)(ptr); +} diff --git a/GPU/GPUTracking/Base/GPUMemoryResource.h b/GPU/GPUTracking/Base/GPUMemoryResource.h index 947bcac504733..79960af9bc188 100644 --- a/GPU/GPUTracking/Base/GPUMemoryResource.h +++ b/GPU/GPUTracking/Base/GPUMemoryResource.h @@ -16,11 +16,14 @@ #define GPUMEMORYRESOURCE_H #include "GPUCommonDef.h" -#include "GPUProcessor.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif namespace o2::gpu { +class GPUProcessor; struct GPUMemoryReuse { enum Type : int32_t { NONE = 0, @@ -80,13 +83,10 @@ class GPUMemoryResource } GPUMemoryResource(const GPUMemoryResource&) = default; - void* SetPointers(void* ptr) - { - return (mProcessor->*mSetPointers)(ptr); - } - void* SetDevicePointers(void* ptr) { return (mProcessor->mLinkedProcessor->*mSetPointers)(ptr); } - void* Ptr() { return mPtr; } - void* PtrDevice() { return mPtrDevice; } + void* SetPointers(void* ptr) const; + void* SetDevicePointers(void* ptr) const; + void* Ptr() const { return mPtr; } + void* PtrDevice() const { return mPtrDevice; } size_t Size() const { return mSize; } const char* Name() const { return mName; } MemoryType Type() const { return mType; } diff --git a/GPU/GPUTracking/Base/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index cc3c6a8bb9140..aa4c3c7671c93 100644 --- a/GPU/GPUTracking/Base/GPUParam.cxx +++ b/GPU/GPUTracking/Base/GPUParam.cxx @@ -18,7 +18,7 @@ #include "GPUCommonMath.h" #include "GPUCommonConstants.h" #include "GPUTPCGMPolynomialFieldManager.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUConstantMem.h" #include "DetectorsBase/Propagator.h" #include "GPUTPCGeometry.h" @@ -35,6 +35,7 @@ void GPUParam::SetDefaults(float solenoidBz, bool assumeConstantBz) memset((void*)this, 0, sizeof(*this)); new (&rec) GPUSettingsRec; occupancyMap = nullptr; + occupancyMapSize = 0; occupancyTotal = 0; #ifdef GPUCA_TPC_GEOMETRY_O2 @@ -126,7 +127,7 @@ void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessi UpdateRun3ClusterErrors(p->param.tpcErrorParamY, p->param.tpcErrorParamZ); } if (w) { - par.dodEdx = dodEdxEnabled = w->steps.isSet(GPUDataTypes::RecoStep::TPCdEdx); + par.dodEdx = dodEdxEnabled = w->steps.isSet(gpudatatypes::RecoStep::TPCdEdx); if (dodEdxEnabled && p && p->tpcDownscaledEdx != 0) { dodEdxEnabled = (rand() % 100) < p->tpcDownscaledEdx; } diff --git a/GPU/GPUTracking/Base/GPUParam.h b/GPU/GPUTracking/Base/GPUParam.h index 2564fc9bf0462..847f3e05ea32e 100644 --- a/GPU/GPUTracking/Base/GPUParam.h +++ b/GPU/GPUTracking/Base/GPUParam.h @@ -63,6 +63,7 @@ struct GPUParam_t { GPUTPCGMPolynomialField polynomialField; // Polynomial approx. of magnetic field for TPC GM const uint32_t* occupancyMap; // Ptr to TPC occupancy map uint32_t occupancyTotal; // Total occupancy in the TPC (nCl / nHbf) + uint32_t occupancyMapSize; // Size of occupancy map GPUParamSector SectorParam[GPUCA_NSECTORS]; diff --git a/GPU/GPUTracking/Base/GPUParam.inc b/GPU/GPUTracking/Base/GPUParam.inc index a118a8f639fe9..dbccca4d7c46b 100644 --- a/GPU/GPUTracking/Base/GPUParam.inc +++ b/GPU/GPUTracking/Base/GPUParam.inc @@ -213,7 +213,7 @@ GPUdi() float GPUParam::GetUnscaledMult(float time) const if (!occupancyMap) { return 0.f; } - const uint32_t bin = CAMath::Max(0.f, time / rec.tpc.occupancyMapTimeBins); + const uint32_t bin = CAMath::Min(occupancyMapSize - 1, CAMath::Max(0.f, time / rec.tpc.occupancyMapTimeBins)); return occupancyMap[bin]; } diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index 6d64fb3daca6a..fbbe815f63c33 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -40,6 +40,7 @@ #include "GPULogging.h" #include "utils/strtag.h" +#include "utils/stdspinlock.h" #ifdef GPUCA_O2_LIB #include "GPUO2InterfaceConfiguration.h" @@ -62,7 +63,7 @@ struct GPUReconstructionPipelineQueue { } // namespace struct GPUReconstructionPipelineContext { - std::queue queue; + std::queue pipelineQueue; std::mutex mutex; std::condition_variable cond; bool terminate = false; @@ -263,7 +264,11 @@ int32_t GPUReconstruction::InitPhaseBeforeDevice() mProcessingSettings->recoTaskTiming = true; } if (GetProcessingSettings().deterministicGPUReconstruction == -1) { +#ifdef GPUCA_DETERMINISTIC_MODE + mProcessingSettings->deterministicGPUReconstruction = 1; +#else mProcessingSettings->deterministicGPUReconstruction = GetProcessingSettings().debugLevel >= 6; +#endif } if (GetProcessingSettings().deterministicGPUReconstruction) { #ifndef GPUCA_DETERMINISTIC_MODE @@ -291,7 +296,7 @@ int32_t GPUReconstruction::InitPhaseBeforeDevice() if (!GetProcessingSettings().createO2Output || !IsGPU()) { mProcessingSettings->clearO2OutputFromGPU = false; } - if (!(mRecoSteps.stepsGPUMask & GPUDataTypes::RecoStep::TPCMerging)) { + if (!(mRecoSteps.stepsGPUMask & gpudatatypes::RecoStep::TPCMerging)) { mProcessingSettings->mergerSortTracks = false; } if (GetProcessingSettings().debugLevel > 3 || !IsGPU() || GetProcessingSettings().deterministicGPUReconstruction) { @@ -566,7 +571,7 @@ size_t GPUReconstruction::AllocateRegisteredPermanentMemory() return total; } -size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, void*& ptr, void*& memorypool, void* memorybase, size_t memorysize, void* (GPUMemoryResource::*setPtr)(void*), void*& memorypoolend, const char* device) +size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, void*& ptr, void*& memorypool, void* memorybase, size_t memorysize, void* (GPUMemoryResource::*setPtr)(void*) const, void*& memorypoolend, const char* device) { if (res->mReuse >= 0) { ptr = (&ptr == &res->mPtrDevice) ? mMemoryResources[res->mReuse].mPtrDevice : mMemoryResources[res->mReuse].mPtr; @@ -589,6 +594,7 @@ size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, throw std::bad_alloc(); } size_t retVal; + stdspinlock spinlock(mMemoryMutex); if ((res->mType & GPUMemoryResource::MEMORY_STACK) && memorypoolend) { retVal = ptrDiff((res->*setPtr)((char*)1), (char*)(1)); memorypoolend = (void*)((char*)memorypoolend - GPUProcessor::getAlignmentMod(memorypoolend)); @@ -639,9 +645,10 @@ void GPUReconstruction::AllocateRegisteredMemoryInternal(GPUMemoryResource* res, res->mPtr = GPUProcessor::alignPointer(res->mPtrDevice); res->SetPointers(res->mPtr); if (GetProcessingSettings().allocDebugLevel >= 2) { - std::cout << (res->mReuse >= 0 ? "Reused " : "Allocated ") << res->mName << ": " << res->mSize << "\n"; + std::cout << (res->mReuse >= 0 ? "Reused " : "Allocated ") << res->mName << ": " << res->mSize << " (individual" << ((res->mType & GPUMemoryResource::MEMORY_STACK) ? " stack" : "") << ")\n"; } if (res->mType & GPUMemoryResource::MEMORY_STACK) { + stdspinlock spinlock(mMemoryMutex); mNonPersistentIndividualAllocations.emplace_back(res); } if ((size_t)res->mPtr % GPUCA_BUFFER_ALIGNMENT) { @@ -722,6 +729,7 @@ size_t GPUReconstruction::AllocateRegisteredMemory(int16_t ires, GPUOutputContro void* GPUReconstruction::AllocateDirectMemory(size_t size, int32_t type) { + stdspinlock spinlock(mMemoryMutex); if (GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { char* retVal = new (std::align_val_t(GPUCA_BUFFER_ALIGNMENT)) char[size]; if ((type & GPUMemoryResource::MEMORY_STACK)) { @@ -744,7 +752,7 @@ void* GPUReconstruction::AllocateDirectMemory(size_t size, int32_t type) void*& poolend = (type & GPUMemoryResource::MEMORY_GPU) ? mDeviceMemoryPoolEnd : mHostMemoryPoolEnd; char* retVal; if ((type & GPUMemoryResource::MEMORY_STACK)) { - poolend = (char*)poolend - size; + poolend = (char*)poolend - size; // TODO: Implement overflow check poolend = (char*)poolend - GPUProcessor::getAlignmentMod(poolend); retVal = (char*)poolend; } else { @@ -756,13 +764,14 @@ void* GPUReconstruction::AllocateDirectMemory(size_t size, int32_t type) } UpdateMaxMemoryUsed(); if (GetProcessingSettings().allocDebugLevel >= 2) { - std::cout << "Allocated (unmanaged " << (type == GPUMemoryResource::MEMORY_GPU ? "gpu" : "host") << "): " << size << " - available: " << ptrDiff(poolend, pool) << "\n"; + std::cout << "Allocated (unmanaged " << ((type & GPUMemoryResource::MEMORY_GPU) ? "gpu" : "host") << "): " << size << " - available: " << ptrDiff(poolend, pool) << "\n"; } return retVal; } void* GPUReconstruction::AllocateVolatileDeviceMemory(size_t size) { + stdspinlock spinlock(mMemoryMutex); if (mVolatileMemoryStart == nullptr) { mVolatileMemoryStart = mDeviceMemoryPool; } @@ -788,6 +797,7 @@ void* GPUReconstruction::AllocateVolatileMemory(size_t size, bool device) return AllocateVolatileDeviceMemory(size); } char* retVal = new (std::align_val_t(GPUCA_BUFFER_ALIGNMENT)) char[size]; + stdspinlock spinlock(mMemoryMutex); mVolatileChunks.emplace_back(retVal, alignedDeleter()); return retVal; } @@ -877,8 +887,11 @@ void GPUReconstruction::PushNonPersistentMemory(uint64_t tag) mNonPersistentMemoryStack.emplace_back(mHostMemoryPoolEnd, mDeviceMemoryPoolEnd, mNonPersistentIndividualAllocations.size(), mNonPersistentIndividualDirectAllocations.size(), tag); } -void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag) +void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag, const GPUProcessor* proc) { + if (proc && GetProcessingSettings().memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + GPUFatal("Processor-depending memory-free works only with allocation strategy ALLOCATION_INDIVIDUAL"); + } if (GetProcessingSettings().keepDisplayMemory || GetProcessingSettings().disableMemoryReuse) { return; } @@ -888,25 +901,34 @@ void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag) if (tag != 0 && std::get<4>(mNonPersistentMemoryStack.back()) != tag) { GPUFatal("Tag mismatch when popping non persistent memory from stack : pop %s vs on stack %s", qTag2Str(tag).c_str(), qTag2Str(std::get<4>(mNonPersistentMemoryStack.back())).c_str()); } - if ((GetProcessingSettings().debugLevel >= 3 || GetProcessingSettings().allocDebugLevel) && (IsGPU() || GetProcessingSettings().forceHostMemoryPoolSize)) { - printf("Allocated memory after %30s (%8s) (Stack %zu): ", GPUDataTypes::RECO_STEP_NAMES[getRecoStepNum(step, true)], qTag2Str(std::get<4>(mNonPersistentMemoryStack.back())).c_str(), mNonPersistentMemoryStack.size()); + if (!proc && (GetProcessingSettings().debugLevel >= 3 || GetProcessingSettings().allocDebugLevel) && (IsGPU() || GetProcessingSettings().forceHostMemoryPoolSize)) { + printf("Allocated memory after %30s (%8s) (Stack %zu): ", gpudatatypes::RECO_STEP_NAMES[getRecoStepNum(step, true)], qTag2Str(std::get<4>(mNonPersistentMemoryStack.back())).c_str(), mNonPersistentMemoryStack.size()); PrintMemoryOverview(); printf("%76s", ""); PrintMemoryMax(); } - mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); - mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); for (uint32_t i = std::get<2>(mNonPersistentMemoryStack.back()); i < mNonPersistentIndividualAllocations.size(); i++) { GPUMemoryResource* res = mNonPersistentIndividualAllocations[i]; + if (proc && res->mProcessor != proc) { + continue; + } + if (GetProcessingSettings().allocDebugLevel >= 2 && (res->mPtr || res->mPtrDevice)) { + std::cout << "Freeing NonPersistent " << res->mName << ": size " << res->mSize << " (reused " << res->mReuse << ")\n"; + } if (res->mReuse < 0) { operator delete(res->mPtrDevice, std::align_val_t(GPUCA_BUFFER_ALIGNMENT)); } res->mPtr = nullptr; res->mPtrDevice = nullptr; } - mNonPersistentIndividualAllocations.resize(std::get<2>(mNonPersistentMemoryStack.back())); - mNonPersistentIndividualDirectAllocations.resize(std::get<3>(mNonPersistentMemoryStack.back())); - mNonPersistentMemoryStack.pop_back(); + if (!proc) { + stdspinlock spinlock(mMemoryMutex); + mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); + mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); + mNonPersistentIndividualAllocations.resize(std::get<2>(mNonPersistentMemoryStack.back())); + mNonPersistentIndividualDirectAllocations.resize(std::get<3>(mNonPersistentMemoryStack.back())); + mNonPersistentMemoryStack.pop_back(); + } } void GPUReconstruction::BlockStackedMemory(GPUReconstruction* rec) @@ -999,7 +1021,7 @@ void GPUReconstruction::PrintMemoryStatistics() } printf("%59s CPU / %9s GPU\n", "", ""); for (auto it = sizes.begin(); it != sizes.end(); it++) { - printf("Allocation %30s %s: Size %'14zu / %'14zu\n", it->first.c_str(), it->second[2] ? "P" : " ", it->second[0], it->second[1]); + printf("Allocation %50s %s: Size %'14zu / %'14zu\n", it->first.c_str(), it->second[2] ? "P" : " ", it->second[0], it->second[1]); } PrintMemoryOverview(); for (uint32_t i = 0; i < mChains.size(); i++) { @@ -1052,8 +1074,8 @@ constexpr static inline int32_t getStepNum(T step, bool validCheck, int32_t N, c } // anonymous namespace } // namespace o2::gpu::internal -int32_t GPUReconstruction::getRecoStepNum(RecoStep step, bool validCheck) { return internal::getStepNum(step, validCheck, GPUDataTypes::N_RECO_STEPS, "Invalid Reco Step"); } -int32_t GPUReconstruction::getGeneralStepNum(GeneralStep step, bool validCheck) { return internal::getStepNum(step, validCheck, GPUDataTypes::N_GENERAL_STEPS, "Invalid General Step"); } +int32_t GPUReconstruction::getRecoStepNum(RecoStep step, bool validCheck) { return internal::getStepNum(step, validCheck, gpudatatypes::N_RECO_STEPS, "Invalid Reco Step"); } +int32_t GPUReconstruction::getGeneralStepNum(GeneralStep step, bool validCheck) { return internal::getStepNum(step, validCheck, gpudatatypes::N_GENERAL_STEPS, "Invalid General Step"); } void GPUReconstruction::RunPipelineWorker() { @@ -1067,13 +1089,13 @@ void GPUReconstruction::RunPipelineWorker() while (!terminate) { { std::unique_lock lk(mPipelineContext->mutex); - mPipelineContext->cond.wait(lk, [this] { return this->mPipelineContext->queue.size() > 0; }); + mPipelineContext->cond.wait(lk, [this] { return this->mPipelineContext->pipelineQueue.size() > 0; }); } GPUReconstructionPipelineQueue* q; { std::lock_guard lk(mPipelineContext->mutex); - q = mPipelineContext->queue.front(); - mPipelineContext->queue.pop(); + q = mPipelineContext->pipelineQueue.front(); + mPipelineContext->pipelineQueue.pop(); } if (q->op == 1) { terminate = 1; @@ -1110,26 +1132,23 @@ int32_t GPUReconstruction::EnqueuePipeline(bool terminate) if (rec->mPipelineContext->terminate) { throw std::runtime_error("Must not enqueue work after termination request"); } - rec->mPipelineContext->queue.push(q); + rec->mPipelineContext->pipelineQueue.push(q); rec->mPipelineContext->terminate = terminate; rec->mPipelineContext->cond.notify_one(); } q->c.wait(lkdone, [&q]() { return q->done; }); - if (q->retVal) { + if (terminate || (q->retVal && (q->retVal != 3 || !GetProcessingSettings().ignoreNonFatalGPUErrors))) { return q->retVal; } - if (terminate) { - return 0; - } else { - return mChains[0]->FinalizePipelinedProcessing(); - } + int32_t retVal2 = mChains[0]->FinalizePipelinedProcessing(); + return retVal2 ? retVal2 : q->retVal; } GPUChain* GPUReconstruction::GetNextChainInQueue() { GPUReconstruction* rec = mMaster ? mMaster : this; std::lock_guard lk(rec->mPipelineContext->mutex); - return rec->mPipelineContext->queue.size() && rec->mPipelineContext->queue.front()->op == 0 ? rec->mPipelineContext->queue.front()->chain : nullptr; + return rec->mPipelineContext->pipelineQueue.size() && rec->mPipelineContext->pipelineQueue.front()->op == 0 ? rec->mPipelineContext->pipelineQueue.front()->chain : nullptr; } void GPUReconstruction::PrepareEvent() // TODO: Clean this up, this should not be called from chainTracking but before @@ -1203,7 +1222,7 @@ void GPUReconstruction::UpdateSettings(const GPUSettingsGRP* g, const GPUSetting mProcessingSettings->resetTimers = p->resetTimers; } GPURecoStepConfiguration* w = nullptr; - if (mRecoSteps.steps.isSet(GPUDataTypes::RecoStep::TPCdEdx)) { + if (mRecoSteps.steps.isSet(gpudatatypes::RecoStep::TPCdEdx)) { w = &mRecoSteps; } param().UpdateSettings(g, p, w, d); diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index 420e602e61352..b5dd29f940143 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -25,14 +25,14 @@ #include #include #include +#include -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUMemoryResource.h" #include "GPUOutputControl.h" - -/*#include "GPUParam.h" -#include "GPUSettings.h" -#include "GPULogging.h"*/ +#include "GPUParam.h" +#include "GPUConstantMem.h" +#include "GPUDef.h" namespace o2::its { @@ -83,12 +83,12 @@ class GPUReconstruction // General definitions constexpr static uint32_t NSECTORS = GPUCA_NSECTORS; - using GeometryType = GPUDataTypes::GeometryType; - using DeviceType = GPUDataTypes::DeviceType; - using RecoStep = GPUDataTypes::RecoStep; - using GeneralStep = GPUDataTypes::GeneralStep; - using RecoStepField = GPUDataTypes::RecoStepField; - using InOutTypeField = GPUDataTypes::InOutTypeField; + using GeometryType = gpudatatypes::GeometryType; + using DeviceType = gpudatatypes::DeviceType; + using RecoStep = gpudatatypes::RecoStep; + using GeneralStep = gpudatatypes::GeneralStep; + using RecoStepField = gpudatatypes::RecoStepField; + using InOutTypeField = gpudatatypes::InOutTypeField; static constexpr const char* const GEOMETRY_TYPE_NAMES[] = {"INVALID", "ALIROOT", "O2"}; #ifdef GPUCA_TPC_GEOMETRY_O2 @@ -179,7 +179,7 @@ class GPUReconstruction void ReturnVolatileMemory(); ThrustVolatileAllocator getThrustVolatileDeviceAllocator(); void PushNonPersistentMemory(uint64_t tag); - void PopNonPersistentMemory(RecoStep step, uint64_t tag); + void PopNonPersistentMemory(RecoStep step, uint64_t tag, const GPUProcessor* proc = nullptr); void BlockStackedMemory(GPUReconstruction* rec); void UnblockStackedMemory(); void ResetRegisteredMemoryPointers(GPUProcessor* proc); @@ -279,7 +279,7 @@ class GPUReconstruction static std::string getBackendVersions(); // Private helper functions for memory management - size_t AllocateRegisteredMemoryHelper(GPUMemoryResource* res, void*& ptr, void*& memorypool, void* memorybase, size_t memorysize, void* (GPUMemoryResource::*SetPointers)(void*), void*& memorypoolend, const char* device); + size_t AllocateRegisteredMemoryHelper(GPUMemoryResource* res, void*& ptr, void*& memorypool, void* memorybase, size_t memorysize, void* (GPUMemoryResource::*SetPointers)(void*) const, void*& memorypoolend, const char* device); size_t AllocateRegisteredPermanentMemory(); // Private helper functions for reading / writing / allocating IO buffer from/to file @@ -390,6 +390,7 @@ class GPUReconstruction std::vector> mNonPersistentIndividualDirectAllocations; std::vector> mDirectMemoryChunks; std::vector> mVolatileChunks; + std::atomic_flag mMemoryMutex = ATOMIC_FLAG_INIT; std::unique_ptr mPipelineContext; diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index 641b0a2d095ca..409c28b8bf328 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -31,6 +31,7 @@ #include "GPULogging.h" #include "GPUMemorySizeScalers.h" #include "GPUReconstructionProcessingKernels.inc" +#include "GPUTPCClusterOccupancyMap.h" #include #include @@ -231,26 +232,24 @@ int32_t GPUReconstructionCPU::RunChains() } mTimerTotal.Start(); const std::clock_t cpuTimerStart = std::clock(); + int32_t retVal = 0; if (GetProcessingSettings().doublePipeline) { - int32_t retVal = EnqueuePipeline(); - if (retVal) { - return retVal; - } + retVal = EnqueuePipeline(); } else { if (mSlaves.size() || mMaster) { WriteConstantParams(); // Reinitialize // TODO: Get this in sync with GPUChainTracking::DoQueuedUpdates, and consider the doublePipeline } for (uint32_t i = 0; i < mChains.size(); i++) { - int32_t retVal = mChains[i]->RunChain(); - if (retVal) { - return retVal; - } - } - if (GetProcessingSettings().tpcFreeAllocatedMemoryAfterProcessing) { - ClearAllocatedMemory(); + retVal = mChains[i]->RunChain(); } } + if (retVal != 0 && retVal != 2) { + return retVal; + } mTimerTotal.Stop(); + if (GetProcessingSettings().tpcFreeAllocatedMemoryAfterProcessing) { + ClearAllocatedMemory(); + } mStatCPUTime += (double)(std::clock() - cpuTimerStart) / CLOCKS_PER_SEC; if (GetProcessingSettings().debugLevel >= 3 || GetProcessingSettings().allocDebugLevel) { GPUInfo("Allocated memory when ending processing %36s", ""); @@ -263,7 +262,7 @@ int32_t GPUReconstructionCPU::RunChains() nEventReport += " (avergage of " + std::to_string(mStatNEvents) + " runs)"; } double kernelTotal = 0; - std::vector kernelStepTimes(GPUDataTypes::N_RECO_STEPS, 0.); + std::vector kernelStepTimes(gpudatatypes::N_RECO_STEPS, 0.); if (GetProcessingSettings().debugLevel >= 1) { for (uint32_t i = 0; i < mTimers.size(); i++) { @@ -297,17 +296,17 @@ int32_t GPUReconstructionCPU::RunChains() } } if (GetProcessingSettings().recoTaskTiming) { - for (int32_t i = 0; i < GPUDataTypes::N_RECO_STEPS; i++) { + for (int32_t i = 0; i < gpudatatypes::N_RECO_STEPS; i++) { if (kernelStepTimes[i] != 0. || mTimersRecoSteps[i].timerTotal.GetElapsedTime() != 0.) { printf("Execution Time: Step : %11s %38s Time: %'10.0f us %64s ( Total Time : %'14.0f us, CPU Time : %'14.0f us, %'7.2fx )\n", "Tasks", - GPUDataTypes::RECO_STEP_NAMES[i], kernelStepTimes[i] * 1000000 / mStatNEvents, "", mTimersRecoSteps[i].timerTotal.GetElapsedTime() * 1000000 / mStatNEvents, mTimersRecoSteps[i].timerCPU * 1000000 / mStatNEvents, mTimersRecoSteps[i].timerCPU / mTimersRecoSteps[i].timerTotal.GetElapsedTime()); + gpudatatypes::RECO_STEP_NAMES[i], kernelStepTimes[i] * 1000000 / mStatNEvents, "", mTimersRecoSteps[i].timerTotal.GetElapsedTime() * 1000000 / mStatNEvents, mTimersRecoSteps[i].timerCPU * 1000000 / mStatNEvents, mTimersRecoSteps[i].timerCPU / mTimersRecoSteps[i].timerTotal.GetElapsedTime()); } if (mTimersRecoSteps[i].bytesToGPU) { - printf("Execution Time: Step (D %8ux): %11s %38s Time: %'10.0f us (%8.3f GB/s - %'14zu bytes - %'14zu per call)\n", mTimersRecoSteps[i].countToGPU, "DMA to GPU", GPUDataTypes::RECO_STEP_NAMES[i], mTimersRecoSteps[i].timerToGPU.GetElapsedTime() * 1000000 / mStatNEvents, + printf("Execution Time: Step (D %8ux): %11s %38s Time: %'10.0f us (%8.3f GB/s - %'14zu bytes - %'14zu per call)\n", mTimersRecoSteps[i].countToGPU, "DMA to GPU", gpudatatypes::RECO_STEP_NAMES[i], mTimersRecoSteps[i].timerToGPU.GetElapsedTime() * 1000000 / mStatNEvents, mTimersRecoSteps[i].bytesToGPU / mTimersRecoSteps[i].timerToGPU.GetElapsedTime() * 1e-9, mTimersRecoSteps[i].bytesToGPU / mStatNEvents, mTimersRecoSteps[i].bytesToGPU / mTimersRecoSteps[i].countToGPU); } if (mTimersRecoSteps[i].bytesToHost) { - printf("Execution Time: Step (D %8ux): %11s %38s Time: %'10.0f us (%8.3f GB/s - %'14zu bytes - %'14zu per call)\n", mTimersRecoSteps[i].countToHost, "DMA to Host", GPUDataTypes::RECO_STEP_NAMES[i], mTimersRecoSteps[i].timerToHost.GetElapsedTime() * 1000000 / mStatNEvents, + printf("Execution Time: Step (D %8ux): %11s %38s Time: %'10.0f us (%8.3f GB/s - %'14zu bytes - %'14zu per call)\n", mTimersRecoSteps[i].countToHost, "DMA to Host", gpudatatypes::RECO_STEP_NAMES[i], mTimersRecoSteps[i].timerToHost.GetElapsedTime() * 1000000 / mStatNEvents, mTimersRecoSteps[i].bytesToHost / mTimersRecoSteps[i].timerToHost.GetElapsedTime() * 1e-9, mTimersRecoSteps[i].bytesToHost / mStatNEvents, mTimersRecoSteps[i].bytesToHost / mTimersRecoSteps[i].countToHost); } if (GetProcessingSettings().resetTimers) { @@ -320,9 +319,9 @@ int32_t GPUReconstructionCPU::RunChains() mTimersRecoSteps[i].countToHost = 0; } } - for (int32_t i = 0; i < GPUDataTypes::N_GENERAL_STEPS; i++) { + for (int32_t i = 0; i < gpudatatypes::N_GENERAL_STEPS; i++) { if (mTimersGeneralSteps[i].GetElapsedTime() != 0.) { - printf("Execution Time: General Step : %50s Time: %'10.0f us\n", GPUDataTypes::GENERAL_STEP_NAMES[i], mTimersGeneralSteps[i].GetElapsedTime() * 1000000 / mStatNEvents); + printf("Execution Time: General Step : %50s Time: %'10.0f us\n", gpudatatypes::GENERAL_STEP_NAMES[i], mTimersGeneralSteps[i].GetElapsedTime() * 1000000 / mStatNEvents); } } if (GetProcessingSettings().debugLevel >= 1) { @@ -339,7 +338,13 @@ int32_t GPUReconstructionCPU::RunChains() mTimerTotal.Reset(); } - return 0; + if (GetProcessingSettings().memoryStat) { + PrintMemoryStatistics(); + } else if (GetProcessingSettings().debugLevel >= 2) { + PrintMemoryOverview(); + } + + return retVal; } void GPUReconstructionCPU::ResetDeviceProcessorTypes() @@ -351,17 +356,25 @@ void GPUReconstructionCPU::ResetDeviceProcessorTypes() } } -void GPUReconstructionCPU::UpdateParamOccupancyMap(const uint32_t* mapHost, const uint32_t* mapGPU, uint32_t occupancyTotal, int32_t stream) +void GPUReconstructionCPU::UpdateParamOccupancyMap(const uint32_t* mapHost, const uint32_t* mapGPU, uint32_t occupancyTotal, uint32_t mapSize, int32_t stream, deviceEvent* ev) { + if (mapHost && mapSize != GPUTPCClusterOccupancyMapBin::getNBins(param())) { + throw std::runtime_error("Updating occupancy map with object of invalid size"); + } param().occupancyMap = mapHost; + param().occupancyMapSize = mapSize; param().occupancyTotal = occupancyTotal; if (IsGPU()) { - if (!((size_t)¶m().occupancyTotal - (size_t)¶m().occupancyMap == sizeof(param().occupancyMap) && sizeof(param().occupancyMap) == sizeof(size_t) && sizeof(param().occupancyTotal) < sizeof(size_t))) { + if (!((size_t)¶m().occupancyMapSize - (size_t)¶m().occupancyMap == sizeof(param().occupancyMap) + sizeof(param().occupancyTotal) && sizeof(param().occupancyMap) == sizeof(void*) && sizeof(param().occupancyTotal) == sizeof(uint32_t))) { // TODO: Make static assert, and check alignment throw std::runtime_error("occupancy data not consecutive in GPUParam"); } + struct tmpOccuapncyParam { + const void* ptr; + uint32_t total; + uint32_t size; + }; + tmpOccuapncyParam tmp = {mapGPU, occupancyTotal, mapSize}; const auto holdContext = GetThreadContext(); - size_t tmp[2] = {(size_t)mapGPU, 0}; - memcpy(&tmp[1], &occupancyTotal, sizeof(occupancyTotal)); - WriteToConstantMemory((char*)&processors()->param.occupancyMap - (char*)processors(), &tmp, sizeof(param().occupancyMap) + sizeof(param().occupancyTotal), stream); + WriteToConstantMemory((char*)&processors()->param.occupancyMap - (char*)processors(), &tmp, sizeof(tmp), stream, ev); } } diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.h b/GPU/GPUTracking/Base/GPUReconstructionCPU.h index 768c301f24327..d621d45fcd92b 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.h +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.h @@ -48,7 +48,7 @@ class GPUReconstructionCPU : public GPUReconstructionProcessing::KernelInterface int32_t RunChains() override; - void UpdateParamOccupancyMap(const uint32_t* mapHost, const uint32_t* mapGPU, uint32_t occupancyTotal, int32_t stream = -1); + void UpdateParamOccupancyMap(const uint32_t* mapHost, const uint32_t* mapGPU, uint32_t occupancyTotal, uint32_t mapSize, int32_t stream = -1, deviceEvent* ev = nullptr); protected: struct GPUProcessorProcessors : public GPUProcessor { @@ -88,8 +88,6 @@ class GPUReconstructionCPU : public GPUReconstructionProcessing::KernelInterface int32_t ExitDevice() override; int32_t GetThread(); - virtual int32_t DoStuckProtection(int32_t stream, deviceEvent event) { return 0; } - // Pointers to tracker classes GPUProcessorProcessors mProcShadow; // Host copy of tracker objects that will be used on the GPU GPUConstantMem*& mProcessorsShadow = mProcShadow.mProcessorsProc; diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPUKernels.h b/GPU/GPUTracking/Base/GPUReconstructionCPUKernels.h index 7bf819a74e1b6..0c19941c40ea4 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPUKernels.h +++ b/GPU/GPUTracking/Base/GPUReconstructionCPUKernels.h @@ -26,8 +26,8 @@ template inline void GPUReconstructionCPU::runKernelInterface(krnlSetup&& setup, Args const&... args) { HighResTimer* t = nullptr; - GPUDataTypes::RecoStep myStep = S::GetRecoStep() == GPUDataTypes::RecoStep::NoRecoStep ? setup.x.step : S::GetRecoStep(); - if (myStep == GPUDataTypes::RecoStep::NoRecoStep) { + gpudatatypes::RecoStep myStep = S::GetRecoStep() == gpudatatypes::RecoStep::NoRecoStep ? setup.x.step : S::GetRecoStep(); + if (myStep == gpudatatypes::RecoStep::NoRecoStep) { throw std::runtime_error("Failure running general kernel without defining RecoStep"); } int32_t cpuFallback = IsGPU() ? (setup.x.device == krnlDeviceType::CPU ? 2 : (mRecoSteps.stepsGPUMask & myStep) != myStep) : 0; diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx index 6bffdc3560d4a..a4b17b81bf5ac 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx @@ -23,7 +23,7 @@ #include "TPCFastTransform.h" #include "GPUTPCClusterData.h" #include "GPUO2DataTypes.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTPCGeometry.h" #include "AliHLTTPCRawCluster.h" // VS: It can not be removed. Used in line 93. #include "GPUParam.h" diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludes.h b/GPU/GPUTracking/Base/GPUReconstructionIncludes.h index d3f11d86a731d..4c057521fe6e7 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludes.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludes.h @@ -24,7 +24,7 @@ #include "GPUDef.h" #include "GPULogging.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include #include diff --git a/GPU/GPUTracking/Base/GPUReconstructionKernelMacros.h b/GPU/GPUTracking/Base/GPUReconstructionKernelMacros.h index a03d9de13ef8f..cc1c62bed507d 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionKernelMacros.h +++ b/GPU/GPUTracking/Base/GPUReconstructionKernelMacros.h @@ -32,6 +32,21 @@ #define GPUCA_M_KRNL_NAME(...) GPUCA_M_KRNL_NAME_A(GPUCA_M_STRIP(__VA_ARGS__)) #if defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_HOSTONLY) + +#if defined(__HIPCC__) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) + static_assert(GPUCA_PAR_AMD_EUS_PER_CU > 0); + #define GPUCA_MIN_WARPS_PER_EU(maxThreadsPerBlock, minBlocksPerCU) GPUCA_CEIL_INT_DIV((minBlocksPerCU) * (maxThreadsPerBlock), (GPUCA_WARP_SIZE * GPUCA_PAR_AMD_EUS_PER_CU)) + + #define GPUCA_LB_ARGS_1(maxThreadsPerBlock) maxThreadsPerBlock + #define GPUCA_LB_ARGS_2(maxThreadsPerBlock, minBlocksPerCU) maxThreadsPerBlock, GPUCA_MIN_WARPS_PER_EU(maxThreadsPerBlock, minBlocksPerCU) + + #define GPUCA_LAUNCH_BOUNDS_SELECT(n, ...) GPUCA_M_CAT(GPUCA_LB_ARGS_, n)(__VA_ARGS__) + #define GPUCA_LAUNCH_BOUNDS_DISP(...) GPUCA_LAUNCH_BOUNDS_SELECT(GPUCA_M_COUNT(__VA_ARGS__), __VA_ARGS__) + #define GPUCA_KRNL_REG_DEFAULT(args) __launch_bounds__(GPUCA_LAUNCH_BOUNDS_DISP(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args)))) +#elif !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) + #define GPUCA_KRNL_REG_DEFAULT(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) +#endif + #ifndef GPUCA_KRNL_REG #define GPUCA_KRNL_REG(...) #endif diff --git a/GPU/GPUTracking/Base/GPUReconstructionLibrary.cxx b/GPU/GPUTracking/Base/GPUReconstructionLibrary.cxx index c70c5d8c51d6f..2e22d4c07e77e 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionLibrary.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionLibrary.cxx @@ -62,15 +62,15 @@ GPUReconstruction* GPUReconstruction::CreateInstance(const GPUSettingsDeviceBack if (retVal == nullptr) { if (cfg.forceDeviceType) { - GPUError("Error: Could not load GPUReconstruction for specified device: %s (%u)", GPUDataTypes::DEVICE_TYPE_NAMES[type], cfg.deviceType); + GPUError("Error: Could not load GPUReconstruction for specified device: %s (%u)", gpudatatypes::DEVICE_TYPE_NAMES[type], cfg.deviceType); } else if (type != DeviceType::CPU) { - GPUError("Could not load GPUReconstruction for device type %s (%u), falling back to CPU version", GPUDataTypes::DEVICE_TYPE_NAMES[type], cfg.deviceType); + GPUError("Could not load GPUReconstruction for device type %s (%u), falling back to CPU version", gpudatatypes::DEVICE_TYPE_NAMES[type], cfg.deviceType); GPUSettingsDeviceBackend cfg2 = cfg; cfg2.deviceType = DeviceType::CPU; retVal = CreateInstance(cfg2); } } else { - GPUInfo("Created GPUReconstruction instance for device type %s (%u)%s", GPUDataTypes::DEVICE_TYPE_NAMES[type], cfg.deviceType, cfg.master ? " (slave)" : ""); + GPUInfo("Created GPUReconstruction instance for device type %s (%u)%s", gpudatatypes::DEVICE_TYPE_NAMES[type], cfg.deviceType, cfg.master ? " (slave)" : ""); } return retVal; @@ -107,14 +107,14 @@ std::shared_ptr* GPUReconstruction::GetLibrary return nullptr; } if (verbose) { - GPUInfo("%s Support not compiled in for device type %u (%s)", GPUDataTypes::DEVICE_TYPE_NAMES[type], (uint32_t)type, GPUDataTypes::DEVICE_TYPE_NAMES[type]); + GPUInfo("%s Support not compiled in for device type %u (%s)", gpudatatypes::DEVICE_TYPE_NAMES[type], (uint32_t)type, gpudatatypes::DEVICE_TYPE_NAMES[type]); } return nullptr; } GPUReconstruction* GPUReconstruction::CreateInstance(const char* type, bool forceType, GPUReconstruction* master) { - DeviceType t = GPUDataTypes::GetDeviceType(type); + DeviceType t = gpudatatypes::GetDeviceType(type); if (t == DeviceType::INVALID_DEVICE) { GPUError("Invalid device type: %s", type); return nullptr; diff --git a/GPU/GPUTracking/Base/GPUReconstructionProcessing.h b/GPU/GPUTracking/Base/GPUReconstructionProcessing.h index 9e611e57148c6..f582610b57973 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionProcessing.h +++ b/GPU/GPUTracking/Base/GPUReconstructionProcessing.h @@ -92,14 +92,14 @@ class GPUReconstructionProcessing : public GPUReconstruction }; struct krnlExec { - constexpr krnlExec(uint32_t b, uint32_t t, int32_t s, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto) : nBlocks(b), nThreads(t), stream(s), device(d), step(GPUDataTypes::RecoStep::NoRecoStep) {} - constexpr krnlExec(uint32_t b, uint32_t t, int32_t s, GPUDataTypes::RecoStep st) : nBlocks(b), nThreads(t), stream(s), device(GPUReconstruction::krnlDeviceType::Auto), step(st) {} - constexpr krnlExec(uint32_t b, uint32_t t, int32_t s, GPUReconstruction::krnlDeviceType d, GPUDataTypes::RecoStep st) : nBlocks(b), nThreads(t), stream(s), device(d), step(st) {} + constexpr krnlExec(uint32_t b, uint32_t t, int32_t s, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto) : nBlocks(b), nThreads(t), stream(s), device(d), step(gpudatatypes::RecoStep::NoRecoStep) {} + constexpr krnlExec(uint32_t b, uint32_t t, int32_t s, gpudatatypes::RecoStep st) : nBlocks(b), nThreads(t), stream(s), device(GPUReconstruction::krnlDeviceType::Auto), step(st) {} + constexpr krnlExec(uint32_t b, uint32_t t, int32_t s, GPUReconstruction::krnlDeviceType d, gpudatatypes::RecoStep st) : nBlocks(b), nThreads(t), stream(s), device(d), step(st) {} uint32_t nBlocks; uint32_t nThreads; int32_t stream; GPUReconstruction::krnlDeviceType device; - GPUDataTypes::RecoStep step; + gpudatatypes::RecoStep step; }; struct krnlRunRange { constexpr krnlRunRange() = default; @@ -198,10 +198,10 @@ class GPUReconstructionProcessing : public GPUReconstruction size_t memSize; // Memory size for memory bandwidth computation }; - HighResTimer mTimersGeneralSteps[GPUDataTypes::N_GENERAL_STEPS]; + HighResTimer mTimersGeneralSteps[gpudatatypes::N_GENERAL_STEPS]; std::vector> mTimers; - RecoStepTimerMeta mTimersRecoSteps[GPUDataTypes::N_RECO_STEPS]; + RecoStepTimerMeta mTimersRecoSteps[gpudatatypes::N_RECO_STEPS]; HighResTimer mTimerTotal; template HighResTimer& getKernelTimer(RecoStep step, int32_t num = 0, size_t addMemorySize = 0, bool increment = true); diff --git a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h index 47cbfa0a1a5b6..14fc949240a7f 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h +++ b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h @@ -16,7 +16,7 @@ #define GPURECONSTRUCTIONTIMEFRAME_H #include "GPUChainTracking.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTPCGeometry.h" #include #include diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 7f595b28a582a..6e54187332c9b 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -41,9 +41,11 @@ set(GPU_RTC_FLAGS "${CMAKE_CUDA_FLAGS} ${CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPP set(GPU_RTC_FLAGS_ARCH "") if(CUDA_COMPUTETARGET) foreach(CUDA_ARCH ${CUDA_COMPUTETARGET}) - set(GPU_RTC_FLAGS_ARCH "${GPU_RTC_FLAGS_ARCH} -gencode arch=compute_${CUDA_ARCH},code=sm_${CUDA_ARCH}") + string(REGEX REPLACE "-.*$" "" CUDA_ARCH_STRIPPED "${CUDA_ARCH}") + set(GPU_RTC_FLAGS_ARCH "${GPU_RTC_FLAGS_ARCH} -gencode arch=compute_${CUDA_ARCH_STRIPPED},code=sm_${CUDA_ARCH_STRIPPED}") endforeach() list (GET CUDA_COMPUTETARGET 0 RTC_CUDA_ARCH) + string(REGEX REPLACE "-.*$" "" RTC_CUDA_ARCH "${RTC_CUDA_ARCH}") set(RTC_CUDA_ARCH "${RTC_CUDA_ARCH}0") else() set(RTC_CUDA_ARCH "750") @@ -146,8 +148,8 @@ if (onnxruntime_FOUND) endif() # Setting target architecture and adding GPU libraries -target_link_libraries(${targetName} PRIVATE cuda cudart nvrtc) -set_target_cuda_arch(${targetName}) +target_link_libraries(${targetName} PRIVATE cuda cudart) +set_target_gpu_arch("CUDA" ${targetName}) #target_link_options(${targetName} PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version_script.ld") #set_target_properties(${targetName} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/version_script.ld) diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index 9e7cfa5495040..c919581eefdde 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -113,13 +113,14 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() constexpr int32_t reqVerMin = 0; #endif if (GetProcessingSettings().rtc.enable && GetProcessingSettings().rtctech.runTest == 2) { - mWarpSize = GPUCA_WARP_SIZE; + mWarpSize = GetProcessingSettings().rtc.overrideWarpSize != -1 ? GetProcessingSettings().rtc.overrideWarpSize : GPUCA_WARP_SIZE; genAndLoadRTC(); exit(0); } if (mMaster == nullptr) { cudaDeviceProp deviceProp; + int deviceMemoryClockRate{0}, deviceClockRate{0}; int32_t count, bestDevice = -1; double bestDeviceSpeed = -1, deviceSpeed; if (GPUChkErrI(cudaGetDeviceCount(&count))) { @@ -153,7 +154,9 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() if (GetProcessingSettings().debugLevel >= 4) { GPUInfo("Obtained current memory usage for device %d", i); } - if (GPUChkErrI(cudaGetDeviceProperties(&deviceProp, i))) { + if (GPUChkErrI(cudaGetDeviceProperties(&deviceProp, i)) || + GPUChkErrI(cudaDeviceGetAttribute(&deviceMemoryClockRate, cudaDevAttrMemoryClockRate, i)) || + GPUChkErrI(cudaDeviceGetAttribute(&deviceClockRate, cudaDevAttrClockRate, i))) { continue; } if (GetProcessingSettings().debugLevel >= 4) { @@ -172,7 +175,7 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() deviceFailure = "Insufficient GPU memory"; } - deviceSpeed = (double)deviceProp.multiProcessorCount * (double)deviceProp.clockRate * (double)deviceProp.warpSize * (double)free * (double)deviceProp.major * (double)deviceProp.major; + deviceSpeed = (double)deviceProp.multiProcessorCount * (double)deviceClockRate * (double)deviceProp.warpSize * (double)free * (double)deviceProp.major * (double)deviceProp.major; if (GetProcessingSettings().debugLevel >= 2) { GPUImportant("Device %s%2d: %s (Rev: %d.%d - Mem Avail %lu / %lu)%s %s", deviceOK ? " " : "[", i, deviceProp.name, deviceProp.major, deviceProp.minor, free, (size_t)deviceProp.totalGlobalMem, deviceOK ? " " : " ]", deviceOK ? "" : deviceFailure); } @@ -186,7 +189,7 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() bestDeviceSpeed = deviceSpeed; } else { if (GetProcessingSettings().debugLevel >= 2 && GetProcessingSettings().deviceNum < 0) { - GPUInfo("Skipping: Speed %f < %f\n", deviceSpeed, bestDeviceSpeed); + GPUInfo("Skipping: Speed %f <= %f\n", deviceSpeed, bestDeviceSpeed); } } } @@ -239,13 +242,13 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() GPUInfo("\ttotalConstMem = %ld", (uint64_t)deviceProp.totalConstMem); GPUInfo("\tmajor = %d", deviceProp.major); GPUInfo("\tminor = %d", deviceProp.minor); - GPUInfo("\tclockRate = %d", deviceProp.clockRate); - GPUInfo("\tmemoryClockRate = %d", deviceProp.memoryClockRate); + GPUInfo("\tclockRate = %d", deviceClockRate); + GPUInfo("\tdeviceMemoryClockRateRate = %d", deviceMemoryClockRate); GPUInfo("\tmultiProcessorCount = %d", deviceProp.multiProcessorCount); GPUInfo("\ttextureAlignment = %ld", (uint64_t)deviceProp.textureAlignment); GPUInfo(" "); } - if (deviceProp.warpSize != GPUCA_WARP_SIZE && !GetProcessingSettings().rtc.enable) { + if (GetProcessingSettings().rtc.enable ? (GetProcessingSettings().rtc.overrideWarpSize != -1 && deviceProp.warpSize != GetProcessingSettings().rtc.overrideWarpSize) : (deviceProp.warpSize != GPUCA_WARP_SIZE)) { throw std::runtime_error("Invalid warp size on GPU"); } mWarpSize = deviceProp.warpSize; @@ -371,7 +374,7 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() #endif mDeviceConstantMem = (GPUConstantMem*)devPtrConstantMem; - GPUInfo("CUDA Initialisation successfull (Device %d: %s (Frequency %d, Cores %d), %ld / %ld bytes host / global memory, Stack frame %d, Constant memory %ld)", mDeviceId, deviceProp.name, deviceProp.clockRate, deviceProp.multiProcessorCount, (int64_t)mHostMemorySize, (int64_t)mDeviceMemorySize, (int32_t)GPUCA_GPU_STACK_SIZE, (int64_t)gGPUConstantMemBufferSize); + GPUInfo("CUDA Initialisation successfull (Device %d: %s (Frequency %d, Cores %d), %ld / %ld bytes host / global memory, Stack frame %d, Constant memory %ld)", mDeviceId, deviceProp.name, deviceClockRate, deviceProp.multiProcessorCount, (int64_t)mHostMemorySize, (int64_t)mDeviceMemorySize, (int32_t)GPUCA_GPU_STACK_SIZE, (int64_t)gGPUConstantMemBufferSize); } else { GPUReconstructionCUDA* master = dynamic_cast(mMaster); mDeviceId = master->mDeviceId; diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx index 8c3fb92c11c9e..dba7e680d0b2c 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx @@ -74,7 +74,7 @@ int32_t GPUReconstructionCUDA::genRTC(std::string& filename, uint32_t& nCompile) } fclose(fp); } - const std::string launchBounds = o2::gpu::internal::GPUDefParametersExport(*mParDevice, true) + + const std::string launchBounds = o2::gpu::internal::GPUDefParametersExport(*mParDevice, true, mParDevice->par_AMD_EUS_PER_CU ? (mParDevice->par_AMD_EUS_PER_CU * mWarpSize) : 0) + "#define GPUCA_WARP_SIZE " + std::to_string(mWarpSize) + "\n"; if (GetProcessingSettings().rtctech.printLaunchBounds || GetProcessingSettings().debugLevel >= 3) { GPUInfo("RTC Launch Bounds:\n%s", launchBounds.c_str()); diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAKernels.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAKernels.cu index e6ed94bba2cec..d668bc7da1513 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAKernels.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAKernels.cu @@ -12,6 +12,7 @@ /// \file GPUReconstructionCUDAKernels.cu /// \author David Rohr +#define GPUCA_COMPILEKERNELS #include "GPUReconstructionCUDAIncludesSystem.h" #include "GPUReconstructionCUDADef.h" @@ -74,7 +75,7 @@ inline void GPUReconstructionCUDA::runKernelBackend(const krnlSetupTime& _xyz, c } #undef GPUCA_KRNL_REG -#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) +#define GPUCA_KRNL_REG(...) GPUCA_KRNL_REG_DEFAULT(__VA_ARGS__) // clang-format off #if defined(GPUCA_KERNEL_COMPILE_MODE) && GPUCA_KERNEL_COMPILE_MODE != 1 // ---------- COMPILE_MODE = perkernel ---------- diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDARTCCalls.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDARTCCalls.cu index 3e4d3113fb995..571428dc39e21 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDARTCCalls.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDARTCCalls.cu @@ -15,7 +15,7 @@ #define GPUCA_GPUCODE_HOSTONLY #define GPUCA_GPUCODE_NO_LAUNCH_BOUNDS -#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) +#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_STRIP(args)) #include "GPUReconstructionCUDAIncludesSystem.h" #include "GPUReconstructionCUDADef.h" diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAkernel.template.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAkernel.template.cu index 847011a70f7f9..82759aab48d70 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAkernel.template.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAkernel.template.cu @@ -14,7 +14,7 @@ #define GPUCA_GPUCODE_COMPILEKERNELS #include "GPUReconstructionCUDAIncludesSystem.h" -#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) +#define GPUCA_KRNL_REG(...) GPUCA_KRNL_REG_DEFAULT(__VA_ARGS__) #define GPUCA_KRNL(...) GPUCA_KRNLGPU(__VA_ARGS__); #include "GPUReconstructionCUDADef.h" #include "GPUReconstructionKernelMacros.h" diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu index 66c02d6ed251c..32286905f2a71 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu @@ -20,7 +20,7 @@ #define GPUCA_DETERMINISTIC_CODE(...) GPUCA_DETERMINISTIC_CODE(__VA_ARGS__) #define GPUCA_RTC_CONSTEXPR GPUCA_RTC_CONSTEXPR -// GPUReconstructionCUDAIncludesSystem.h prependended by CMakewithout preprocessor running +// GPUReconstructionCUDAIncludesSystem.h prependended by CMake without preprocessor running #include "GPUReconstructionCUDADef.h" #include "GPUReconstructionIncludesDeviceAll.h" diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index 17bbf46795761..50d710fd9d557 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -11,6 +11,12 @@ set(MODULE GPUTrackingHIP) +if(GPUCA_BUILD_DEBUG AND GPUCA_BUILD_DEBUG_HOSTONLY) + set(CMAKE_BUILD_TYPE RELEASE) + set(CMAKE_HIP_FLAGS_RELEASE "${CMAKE_HIP_FLAGS_RELEASE} -O3 -march=native -ggdb -fno-sanitize=all -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -ggdb -fno-sanitize=all -DNDEBUG") +endif() + # -------------------------------- Options ------------------------------------------------------- # set(GPUCA_HIP_HIPIFY_FROM_CUDA 0) # Use local HIP source files @@ -213,7 +219,7 @@ endif() # Setting target architecture and adding GPU libraries target_link_libraries(${targetName} PRIVATE hip::host hip::device hip::hipcub roc::rocthrust) -set_target_hip_arch(${targetName}) +set_target_gpu_arch("HIP" ${targetName}) target_link_libraries(${MODULE}_CXX PRIVATE TBB::tbb) diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h b/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h index 0228f993aaee3..389c79c0e4eb9 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h @@ -22,11 +22,8 @@ #include #include #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" // FIXME: Is this still needed? #include #include #include -#pragma GCC diagnostic pop #endif // O2_GPU_RECONSTRUCTIONHIPINCLUDESSYSTEM_H diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIPkernel.template.hip b/GPU/GPUTracking/Base/hip/GPUReconstructionHIPkernel.template.hip index 30a84dfa135eb..7cb895cadd770 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIPkernel.template.hip +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIPkernel.template.hip @@ -14,7 +14,7 @@ #define GPUCA_GPUCODE_COMPILEKERNELS #include "GPUReconstructionHIPIncludesSystem.h" -#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) +#define GPUCA_KRNL_REG(...) GPUCA_KRNL_REG_DEFAULT(__VA_ARGS__) #define GPUCA_KRNL(...) GPUCA_KRNLGPU(__VA_ARGS__); #include "GPUReconstructionHIPDef.h" #include "GPUReconstructionKernelMacros.h" diff --git a/GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip.cxx b/GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip similarity index 100% rename from GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip.cxx rename to GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip diff --git a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.cxx b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.cxx index 271fe494860cd..6954cfb3d6211 100644 --- a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.cxx +++ b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.cxx @@ -470,27 +470,6 @@ void GPUReconstructionOCL::ReleaseEvent(deviceEvent ev) { GPUChkErr(clReleaseEve void GPUReconstructionOCL::RecordMarker(deviceEvent* ev, int32_t stream) { GPUChkErr(clEnqueueMarkerWithWaitList(mInternals->command_queue[stream], 0, nullptr, ev->getEventList())); } -int32_t GPUReconstructionOCL::DoStuckProtection(int32_t stream, deviceEvent event) -{ - if (GetProcessingSettings().stuckProtection) { - cl_int tmp = 0; - for (int32_t i = 0; i <= GetProcessingSettings().stuckProtection / 50; i++) { - usleep(50); - clGetEventInfo(event.get(), CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(tmp), &tmp, nullptr); - if (tmp == CL_COMPLETE) { - break; - } - } - if (tmp != CL_COMPLETE) { - mGPUStuck = 1; - GPUErrorReturn("GPU Stuck, future processing in this component is disabled, skipping event (GPU Event State %d)", (int32_t)tmp); - } - } else { - clFinish(mInternals->command_queue[stream]); - } - return 0; -} - void GPUReconstructionOCL::SynchronizeGPU() { for (int32_t i = 0; i < mNStreams; i++) { diff --git a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.h b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.h index 958d5186bf41a..a52db1f2a737a 100644 --- a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.h +++ b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL.h @@ -43,7 +43,6 @@ class GPUReconstructionOCL : public GPUReconstructionProcessing::KernelInterface virtual int32_t GPUChkErrInternal(const int64_t error, const char* file, int32_t line) const override; void SynchronizeGPU() override; - int32_t DoStuckProtection(int32_t stream, deviceEvent event) override; int32_t GPUDebug(const char* state = "UNKNOWN", int32_t stream = -1, bool force = false) override; void SynchronizeStream(int32_t stream) override; void SynchronizeEvents(deviceEvent* evList, int32_t nEvents = 1) override; diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index a976e5c93b7c6..dfee81b398a79 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -59,16 +59,17 @@ set(SRCS Merger/GPUTPCGMPhysicalTrackModel.cxx Merger/GPUTPCGMPolynomialFieldManager.cxx DataTypes/GPUTRDTrack.cxx + DataTypes/GPUTRDRecoParam.cxx TRDTracking/GPUTRDTracker.cxx TRDTracking/GPUTRDTrackletWord.cxx TRDTracking/GPUTRDTrackerKernels.cxx Base/GPUParam.cxx) -set(SRCS_DATATYPES DataTypes/GPUDataTypes.cxx DataTypes/GPUConfigDump.cxx DataTypes/GPUTPCGMPolynomialField.cxx) +set(SRCS_DATATYPES DataTypes/GPUDataTypesConfig.cxx DataTypes/GPUConfigDump.cxx DataTypes/GPUTPCGMPolynomialField.cxx) set(HDRS_CINT_O2 Merger/GPUTPCGMTrackParam.h Merger/GPUTPCGMMergedTrack.h Merger/GPUTPCGMSectorTrack.h Merger/GPUTPCGMBorderTrack.h TRDTracking/GPUTRDInterfaces.h) set(HDRS_CINT_DATATYPES DataTypes/GPUTPCGMMergedTrackHit.h) -set(HDRS_CINT_O2_ADDITIONAL DataTypes/GPUSettings.h Definitions/GPUSettingsList.h DataTypes/GPUDataTypes.h DataTypes/GPUTRDTrack.h DataTypes/CalibdEdxTrackTopologyPol.h DataTypes/CalibdEdxTrackTopologySpline.h) # Manual dependencies for ROOT dictionary generation +set(HDRS_CINT_O2_ADDITIONAL DataTypes/GPUSettings.h Definitions/GPUSettingsList.h DataTypes/GPUDataTypesIO.h DataTypes/GPUDataTypesConfig.h DataTypes/GPUDataTypesQA.h DataTypes/GPUTRDTrack.h DataTypes/GPUTRDRecoParam.h DataTypes/CalibdEdxTrackTopologyPol.h DataTypes/CalibdEdxTrackTopologySpline.h) # Manual dependencies for ROOT dictionary generation set(SRCS_NO_CINT DataTypes/GPUMemorySizeScalers.cxx @@ -107,6 +108,9 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingIO.cxx) set(HDRS_INSTALL + ${HDRS_CINT_O2} + ${HDRS_CINT_DATATYPES} + ${HDRS_CINT_O2_ADDITIONAL} Base/GPUConstantMem.h Base/GPUParam.inc Base/GPUParamRTC.h @@ -123,28 +127,23 @@ set(HDRS_INSTALL DataTypes/GPUHostDataTypes.h DataTypes/GPUO2DataTypes.h DataTypes/GPUOutputControl.h - DataTypes/GPUSettings.h DataTypes/GPUTPCGeometry.h - DataTypes/GPUTPCGMMergedTrackHit.h DataTypes/GPUTRDDef.h DataTypes/GPUTRDInterfaceO2Track.h DataTypes/GPUTriggerOutputs.h DataTypes/GPUKernelClassesFwd.h + DataTypes/GPUO2ExternalUser.h Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h - Definitions/GPUDefParametersDefaults.h Definitions/GPUDef.h Definitions/GPUDefMacros.h Definitions/GPULogging.h - Definitions/GPUSettingsList.h Global/GPUChainTrackingDefs.h Global/GPUChainTrackingDebug.h Global/GPUChainTrackingGetters.inc Global/GPUErrorCodes.h - Merger/GPUTPCGMBorderTrack.h - Merger/GPUTPCGMMergedTrack.h Merger/GPUTPCGMMergerTypes.h qa/GPUQAHelper.h qconfigoptions.h @@ -159,7 +158,6 @@ set(HDRS_INSTALL SectorTracker/GPUTPCTrackLinearisation.h TPCConvert/GPUTPCConvertImpl.h TRDTracking/GPUTRDGeometry.h - TRDTracking/GPUTRDInterfaces.h TRDTracking/GPUTRDSpacePoint.h TRDTracking/GPUTRDTrackData.h TRDTracking/GPUTRDTrackerDebug.h @@ -208,6 +206,7 @@ set(SRCS_DATATYPES DataTypes/TPCPadBitMap.cxx DataTypes/TPCZSLinkMapping.cxx DataTypes/CalibdEdxContainer.cxx + DataTypes/ORTRootSerializer.cxx DataTypes/CalibdEdxTrackTopologyPol.cxx DataTypes/CalibdEdxTrackTopologySpline.cxx DataTypes/GPUTRDTrackO2.cxx) @@ -228,9 +227,11 @@ set(HDRS_INSTALL ${HDRS_INSTALL} if(ALIGPU_BUILD_TYPE STREQUAL "O2") set(SRCS_DATATYPES ${SRCS_DATATYPES} - Interface/GPUO2InterfaceConfigurableParam.cxx) + DataTypes/GPUO2ConfigurableParam.cxx) endif() +set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) +file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) set(TEMPLATE_HEADER_LIST Base/GPUReconstructionKernelList.template.h Base/GPUReconstructionKernelIncludes.template.h Base/GPUReconstructionIncludesDeviceAll.template.h @@ -239,8 +240,6 @@ set(TEMPLATE_HEADER_LIST Base/GPUReconstructionKernelList.template.h Definitions/GPUDefParametersLoad.template.inc) set(GENERATED_HEADERS_LIST "") -set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) -file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) foreach(TEMPLATE_FILE ${TEMPLATE_HEADER_LIST}) get_filename_component(OUTPUT_FILE_NAME ${TEMPLATE_FILE} NAME) string(REPLACE ".template" "" OUTPUT_FILE_NAME ${OUTPUT_FILE_NAME}) @@ -263,6 +262,42 @@ add_custom_command( ) list(APPEND GENERATED_HEADERS_LIST ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h) +if(GPUCA_OVERRIDE_PARAMETER_FILE) + set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) +else() + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) +endif() +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GPU_PARAM_JSON}") + +set(GPU_PARAM_JSON_FILES) +set(GPU_PARAM_JSON_N_FILES 0) +foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON) + if(NOT EXISTS "${GPU_PARAM_JSON_FILE}") + message(FATAL_ERROR "Parameter file ${GPU_PARAM_JSON_FILE} does not exist") + endif() + get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON_FILE} EXT) + string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) + if(GPU_PARAM_JSON_EXT STREQUAL .csv) + get_filename_component(GPU_PARAM_JSON_NAME ${GPU_PARAM_JSON_FILE} NAME_WE) + set(CONVOUTFILE "GPUParameters_${GPU_PARAM_JSON_NAME}_${GPU_PARAM_JSON_N_FILES}.json") + execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON_FILE}" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + message(STATUS "Converted ${GPU_PARAM_JSON_FILE} to ${CONVOUTFILE}") + list(APPEND GPU_PARAM_JSON_FILES ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE}) + else() + list(APPEND GPU_PARAM_JSON_FILES ${GPU_PARAM_JSON_FILE}) + endif() + math(EXPR GPU_PARAM_JSON_N_FILES "${GPU_PARAM_JSON_N_FILES} + 1") +endforeach() + +include(Definitions/Parameters/gpu_param_header_generator.cmake) +set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) +set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) +generate_gpu_param_header("${GPU_PARAM_JSON_FILES}" "ALL" "${GPU_DEFAULT_PARAMS_HEADER}" "${GPU_DEFAULT_PARAMS_HEADER_DEVICE}" GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters for all architectures +list(APPEND GENERATED_HEADERS_LIST ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE}) + set(HDRS_INSTALL ${HDRS_INSTALL} ${GENERATED_HEADERS_LIST}) include(kernels.cmake) @@ -286,6 +321,7 @@ set(HDRS_CINT_DATATYPES ${HDRS_CINT_DATATYPES} ${HDRS_TMP}) unset(HDRS_TMP) set(INCDIRS + ${ON_THE_FLY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Definitions ${CMAKE_CURRENT_SOURCE_DIR}/DataTypes @@ -302,14 +338,14 @@ set(INCDIRS ${CMAKE_CURRENT_SOURCE_DIR}/Refit ${CMAKE_CURRENT_SOURCE_DIR}/Debug ${CMAKE_CURRENT_SOURCE_DIR}/DataCompression - ${CMAKE_CURRENT_SOURCE_DIR}/TPCClusterFinder - ${ON_THE_FLY_DIR}) + ${CMAKE_CURRENT_SOURCE_DIR}/TPCClusterFinder) # Main CMake part for O2 if(ALIGPU_BUILD_TYPE STREQUAL "O2") o2_add_library(GPUDataTypes TARGETVARNAME targetName PUBLIC_INCLUDE_DIRECTORIES . + ${ON_THE_FLY_DIR} Definitions DataTypes PUBLIC_LINK_LIBRARIES O2::GPUUtils @@ -404,30 +440,6 @@ target_sources(${targetName} FILES ${GENERATED_HEADERS_LIST} BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) -make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) -set(GPU_CONST_PARAM_ARCHITECTUES AMPERE TURING VEGA MI2xx) -set(GPU_CONST_PARAM_FILES "") -foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) - set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) - add_custom_command( - OUTPUT ${PARAMFILE} - COMMAND bash -c - "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"GPUDefParametersDefaults.h\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" - | root -l -b > /dev/null - VERBATIM - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch - MAIN_DEPENDENCY Standalone/tools/dumpGPUDefParam.C - DEPENDS Definitions/GPUDefParametersDefaults.h - ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h - ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc - COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") - LIST(APPEND GPU_CONST_PARAM_FILES ${PARAMFILE}) -endforeach() - -add_custom_target(${MODULE}_GPU_CONST_PARAM_ARCHS ALL DEPENDS ${GPU_CONST_PARAM_FILES}) -install(FILES ${GPU_CONST_PARAM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/arch_param) - - # Add compile definitions and libraries depending on available optional dependencies if(GPUCA_QA) message(STATUS "Building GPU QA") @@ -448,6 +460,28 @@ if(CUDA_ENABLED OR OPENCL_ENABLED OR HIP_ENABLED) if(CMAKE_SYSTEM_NAME MATCHES Darwin) message(WARNING "GPU Tracking disabled on MacOS") else() + make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) + set(GPU_CONST_PARAM_FILES) + foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) + set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) + add_custom_command( + OUTPUT ${PARAMFILE} + COMMAND bash -c + "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"GPUDefParametersDefaults.h\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" + | root -l -b > /dev/null + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch + MAIN_DEPENDENCY Standalone/tools/dumpGPUDefParam.C + DEPENDS ${GPU_DEFAULT_PARAMS_HEADER} + ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} + ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h + ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc + COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") + LIST(APPEND GPU_CONST_PARAM_FILES ${PARAMFILE}) + endforeach() + add_custom_target(${MODULE}_GPU_CONST_PARAM_ARCHS ALL DEPENDS ${GPU_CONST_PARAM_FILES}) + install(FILES ${GPU_CONST_PARAM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/arch_param) + if(CUDA_ENABLED) add_subdirectory(Base/cuda) endif() diff --git a/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h b/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h index f39994f2d1045..8ec8c85c4c73f 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h @@ -16,29 +16,36 @@ #define GPUTPCCLUSTERREJECTION_H #include "GPUTPCGMMergerTypes.h" +#include "GPUCommonMath.h" namespace o2::gpu { struct GPUTPCClusterRejection { + template + GPUdi() static bool IsTrackRejected(const T& trk, const S& param) + { + return CAMath::Abs(trk.GetParam().GetQPt() * param.qptB5Scaler) > param.rec.tpc.rejectQPtB5 || trk.MergedLooper(); + } + template - static constexpr inline bool GetProtectionStatus(int32_t attach, bool& physics, bool& protect, T* counts = nullptr, S* mev200 = nullptr) + GPUdi() static constexpr bool GetRejectionStatus(int32_t attach, bool& physics, T* counts = nullptr, S* mev200 = nullptr) { (void)counts; // FIXME: Avoid incorrect -Wunused-but-set-parameter warning (void)mev200; + bool retVal = false; if (attach == 0) { - return false; + retVal = false; } else if ((attach & gputpcgmmergertypes::attachGoodLeg) == 0) { if constexpr (C) { counts->nLoopers++; } - return true; + retVal = true; } else if (attach & gputpcgmmergertypes::attachHighIncl) { if constexpr (C) { counts->nHighIncl++; } - return true; + retVal = true; } else if (attach & gputpcgmmergertypes::attachTube) { - protect = true; if constexpr (C) { if (*mev200) { counts->nTube200++; @@ -46,23 +53,27 @@ struct GPUTPCClusterRejection { counts->nTube++; } } - return false; + retVal = false; } else if ((attach & gputpcgmmergertypes::attachGood) == 0) { - protect = true; if constexpr (C) { counts->nRejected++; } - return false; + retVal = false; } else { physics = true; - return false; + retVal = false; + } + + if (attach & gputpcgmmergertypes::attachProtect) { + retVal = false; } + return retVal; } - static constexpr inline bool GetIsRejected(int32_t attach) + GPUdi() static constexpr bool GetIsRejected(int32_t attach) { - bool physics = false, protect = false; - return GetProtectionStatus(attach, physics, protect); + bool physics = false; + return GetRejectionStatus(attach, physics); } }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx index 61f8a614fbe6f..efb7a4af3f323 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx @@ -38,7 +38,7 @@ void* GPUTPCCompression::SetPointersOutputHost(void* mem) void* GPUTPCCompression::SetPointersScratch(void* mem) { - int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; + int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; computePointerWithAlignment(mem, mClusterStatus, mMaxClusters); if (gatherMode >= 2) { computePointerWithAlignment(mem, mAttachedClusterFirstIndex, mMaxTracks); @@ -51,7 +51,7 @@ void* GPUTPCCompression::SetPointersScratch(void* mem) void* GPUTPCCompression::SetPointersOutput(void* mem) { - int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; + int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; computePointerWithAlignment(mem, mAttachedClusterFirstIndex, mMaxTrackClusters); if (gatherMode == 1) { SetPointersCompressedClusters(mem, mPtrs, mMaxTrackClusters, mMaxTracks, mMaxClustersInCache, false); @@ -106,12 +106,12 @@ void* GPUTPCCompression::SetPointersMemory(void* mem) void GPUTPCCompression::RegisterMemoryAllocation() { AllocateAndInitializeLate(); - int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; + int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; mMemoryResOutputHost = mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutputHost, GPUMemoryResource::MEMORY_OUTPUT_FLAG | GPUMemoryResource::MEMORY_HOST | GPUMemoryResource::MEMORY_CUSTOM, "TPCCompressionOutputHost"); if (gatherMode == 3) { mMemoryResOutputGPU = mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutputGPU, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_STACK, "TPCCompressionOutputGPU"); } - uint32_t stackScratch = (gatherMode != 3) ? GPUMemoryResource::MEMORY_STACK : 0; + uint32_t stackScratch = (gatherMode != 3) ? GPUMemoryResource::MEMORY_STACK : 0; // TODO: Can we use stacked memory also with gather mode 3? if (gatherMode < 2) { mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutput, GPUMemoryResource::MEMORY_OUTPUT | stackScratch, "TPCCompressionOutput"); } diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompression.h b/GPU/GPUTracking/DataCompression/GPUTPCCompression.h index 1dafffaeea043..95173dad7257f 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompression.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompression.h @@ -60,6 +60,7 @@ class GPUTPCCompression : public GPUProcessor #ifndef GPUCA_GPUCODE void DumpCompressedClusters(std::ostream& out); #endif + GPUd() bool rejectCluster(int32_t idx, const GPUParam& param, const GPUTrackingInOutPointers& ioPtrs) const; protected: struct memory { @@ -89,7 +90,6 @@ class GPUTPCCompression : public GPUProcessor void SetPointersCompressedClusters(void*& mem, T& c, uint32_t nClA, uint32_t nTr, uint32_t nClU, bool reducedClA); template GPUd() static void truncateSignificantBits(T& val, uint32_t nBits, uint32_t max); - GPUd() bool rejectCluster(int32_t idx, GPUParam& param, const GPUTrackingInOutPointers& ioPtrs); int16_t mMemoryResOutputHost = -1; int16_t mMemoryResOutputGPU = -1; diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx index d5567bb5148d9..1cd44e221f203 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx @@ -39,7 +39,7 @@ GPUdii() void GPUTPCCompressionKernels::Thread processors.param.rec.tpc.rejectQPtB5 || trk.MergedLooper(); + bool rejectTrk = GPUTPCClusterRejection::IsTrackRejected(trk, param); uint32_t nClustersStored = 0; CompressedClustersPtrs& GPUrestrict() c = compressor.mPtrs; uint8_t lastRow = 0, lastSector = 0; @@ -56,10 +56,12 @@ GPUdii() void GPUTPCCompressionKernels::Thread= GPUSettings::RejectionStrategyA && (rejectTrk || GPUTPCClusterRejection::GetIsRejected(attach)); + bool rejectCluster = processors.param.rec.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyA && !(attach & gputpcgmmergertypes::attachProtect) && (rejectTrk || GPUTPCClusterRejection::GetIsRejected(attach)); if (rejectCluster) { compressor.mClusterStatus[hitId] = 1; // Cluster rejected, do not store continue; + } else if (processors.param.rec.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyA && rejectTrk) { + continue; } if (!(param.rec.tpc.compressionTypeMask & GPUSettings::CompressionTrackModel)) { @@ -183,7 +185,7 @@ GPUd() bool GPUTPCCompressionKernels::GPUTPCCompressionKernels_Compare<4>::opera return mClsPtr[a].qTot < mClsPtr[b].qTot; } -GPUd() bool GPUTPCCompression::rejectCluster(int32_t idx, GPUParam& GPUrestrict() param, const GPUTrackingInOutPointers& GPUrestrict() ioPtrs) +GPUd() bool GPUTPCCompression::rejectCluster(int32_t idx, const GPUParam& GPUrestrict() param, const GPUTrackingInOutPointers& GPUrestrict() ioPtrs) const { if (mClusterStatus[idx]) { return true; @@ -199,9 +201,12 @@ GPUd() bool GPUTPCCompression::rejectCluster(int32_t idx, GPUParam& GPUrestrict( if (GPUTPCClusterRejection::GetIsRejected(attach)) { return true; } + if (attach & gputpcgmmergertypes::attachProtect) { + return false; + } int32_t id = attach & gputpcgmmergertypes::attachTrackMask; auto& trk = ioPtrs.mergedTracks[id]; - if (CAMath::Abs(trk.GetParam().GetQPt() * param.qptB5Scaler) > param.rec.tpc.rejectQPtB5 || trk.MergedLooper()) { + if (GPUTPCClusterRejection::IsTrackRejected(trk, param)) { return true; } } diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h index 81817abf1e6d6..2236f15af9725 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h @@ -27,7 +27,7 @@ namespace o2::gpu class GPUTPCCompressionKernels : public GPUKernelTemplate { public: - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCCompression; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCCompression; } enum K : int32_t { step0attached = 0, diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx index fd0c929dd2ba7..b2949b5a98fd2 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx @@ -18,6 +18,7 @@ #include "GPUO2DataTypes.h" #include "GPUMemorySizeScalers.h" #include "GPULogging.h" +#include using namespace o2::gpu; @@ -106,15 +107,20 @@ void GPUTPCDecompression::RegisterMemoryAllocation() { AllocateAndInitializeLate(); mMemoryResInputGPU = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputGPU, GPUMemoryResource::MEMORY_INPUT_FLAG | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_EXTERNAL | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionInput"); - mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersGPU, GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersGPU"); - mResourceTmpIndexes = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersOutput"); - mResourceTmpClustersOffsets = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersInput"); - mResourceTmpBufferBeforeFiltering = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpClusterNativeAccessForFiltering, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBufferForFiltering"); - mResourceClusterNativeAccess = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputClusterNativeAccess, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpClusterAccessForFiltering"); - mResourceNClusterPerSectorRow = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersNClusterPerSectorRow, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpClusterCountForFiltering"); + mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersGPU, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBuffersGPU"); + mResourceTmpIndexes = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBuffersOutput"); + mResourceTmpClustersOffsets = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBuffersInput"); + mResourceTmpBufferBeforeFiltering = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpClusterNativeAccessForFiltering, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBufferForFiltering"); + mResourceClusterNativeAccess = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputClusterNativeAccess, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpClusterAccessForFiltering"); + mResourceNClusterPerSectorRow = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersNClusterPerSectorRow, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpClusterCountForFiltering"); } void GPUTPCDecompression::SetMaxData(const GPUTrackingInOutPointers& io) { - mMaxNativeClustersPerBuffer = mRec->GetProcessingSettings().tpcMaxAttachedClustersPerSectorRow; + uint32_t maxAttachedClsMargin1 = *std::max_element(mInputGPU.nSliceRowClusters, mInputGPU.nSliceRowClusters + mInputGPU.nSliceRows); + float clsRatio1 = (mInputGPU.nUnattachedClusters > 0 ? float(mInputGPU.nAttachedClusters) / float(mInputGPU.nUnattachedClusters) : 1.0f) * 1.5f; + maxAttachedClsMargin1 *= clsRatio1; + uint32_t maxAttachedClsMargin2 = mInputGPU.nSliceRows > 0 ? (mInputGPU.nAttachedClusters / mInputGPU.nSliceRows * 3.5) : 0; // mean #attached cls per SectorRow multiplied by 3.5 (tuned) + mMaxNativeClustersPerBuffer = std::max({maxAttachedClsMargin1, maxAttachedClsMargin2, 1000u}); // take biggest margin, 1000 clusters minimum + mMaxNativeClustersPerBuffer = std::min(mMaxNativeClustersPerBuffer, mRec->GetProcessingSettings().tpcMaxAttachedClustersPerSectorRow); // upperbound given by configurable param } diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h index 1ea93e4acb9d0..2140cfbe5166d 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h @@ -27,7 +27,7 @@ namespace o2::gpu class GPUTPCDecompressionKernels : public GPUKernelTemplate { public: - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCDecompression; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCDecompression; } enum K : int32_t { step0attached = 0, diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx index cd1717faf178d..51a896c2baf6a 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx @@ -43,10 +43,10 @@ int32_t TPCClusterDecompressor::decompress(const CompressedClustersFlat* cluster int32_t TPCClusterDecompressor::decompress(const CompressedClusters* clustersCompressed, o2::tpc::ClusterNativeAccess& clustersNative, std::function allocator, const GPUParam& param, bool deterministicRec) { if (clustersCompressed->nTracks && clustersCompressed->solenoidBz != -1e6f && clustersCompressed->solenoidBz != param.bzkG) { - throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); + throw std::runtime_error("Configured solenoid Bz " + std::to_string(param.bzkG) + " does not match value used for track model encoding " + std::to_string(clustersCompressed->solenoidBz)); } if (clustersCompressed->nTracks && clustersCompressed->maxTimeBin != -1e6 && clustersCompressed->maxTimeBin != param.continuousMaxTimeBin) { - throw std::runtime_error("Configured max time bin does not match value used for track model encoding"); + throw std::runtime_error("Configured max time bin " + std::to_string(param.continuousMaxTimeBin) + " does not match value used for track model encoding " + std::to_string(clustersCompressed->maxTimeBin)); } std::vector clusters[NSECTORS][GPUCA_ROW_COUNT]; std::atomic_flag locks[NSECTORS][GPUCA_ROW_COUNT]; diff --git a/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx b/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx index 7ec2df3a2f186..56543d5f2e43d 100644 --- a/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx +++ b/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx @@ -13,7 +13,7 @@ /// \author David Rohr #include "GPUConfigDump.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUSettings.h" #include diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypes.cxx b/GPU/GPUTracking/DataTypes/GPUDataTypesConfig.cxx similarity index 73% rename from GPU/GPUTracking/DataTypes/GPUDataTypes.cxx rename to GPU/GPUTracking/DataTypes/GPUDataTypesConfig.cxx index c544ac610cdfa..80ca919dd29e1 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypes.cxx +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesConfig.cxx @@ -9,19 +9,15 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUDataTypes.cxx +/// \file GPUDataTypesConfig.cxx /// \author David Rohr -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" #include using namespace o2::gpu; -constexpr const char* const GPUDataTypes::DEVICE_TYPE_NAMES[]; -constexpr const char* const GPUDataTypes::RECO_STEP_NAMES[]; -constexpr const char* const GPUDataTypes::GENERAL_STEP_NAMES[]; - -GPUDataTypes::DeviceType GPUDataTypes::GetDeviceType(const char* type) +gpudatatypes::DeviceType gpudatatypes::GetDeviceType(const char* type) { for (uint32_t i = 1; i < sizeof(DEVICE_TYPE_NAMES) / sizeof(DEVICE_TYPE_NAMES[0]); i++) { if (strcmp(DEVICE_TYPE_NAMES[i], type) == 0) { diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypesConfig.h b/GPU/GPUTracking/DataTypes/GPUDataTypesConfig.h new file mode 100644 index 0000000000000..6535bb93770c4 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesConfig.h @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDataTypesConfig.h +/// \author David Rohr + +#ifndef GPUDATATYPESCONFIG_H +#define GPUDATATYPESCONFIG_H + +#include "GPUCommonDef.h" + +// These are basic and non-complex data types, which will also be visible on the GPU. +// Please add complex data types required on the host but not GPU to GPUHostDataTypes.h and forward-declare! +#ifndef GPUCA_GPUCODE_DEVICE +#include // for bitfield below +#include +#endif + +namespace o2::gpu +{ +#include "utils/bitfield.h" + +namespace gpudatatypes +{ +// clang-format off +enum class GeometryType : uint32_t { RESERVED_GEOMETRY = 0, ALIROOT = 1, O2 = 2 }; +enum DeviceType : uint32_t { INVALID_DEVICE = 0, CPU = 1, CUDA = 2, HIP = 3, OCL = 4 }; +enum class GeneralStep : uint32_t { Prepare = 1, QA = 2 }; +// clang-format on + +enum class RecoStep : uint32_t { TPCConversion = 1, + TPCSectorTracking = 2, + TPCMerging = 4, + TPCCompression = 8, + TRDTracking = 16, + ITSTracking = 32, + TPCdEdx = 64, + TPCClusterFinding = 128, + TPCDecompression = 256, + Refit = 512, + AllRecoSteps = 0x7FFFFFFF, + NoRecoStep = 0 }; +enum class InOutType : uint32_t { TPCClusters = 1, + OBSOLETE = 2, + TPCMergedTracks = 4, + TPCCompressedClusters = 8, + TRDTracklets = 16, + TRDTracks = 32, + TPCRaw = 64, + ITSClusters = 128, + ITSTracks = 256 }; +#ifndef __OPENCL__ +static constexpr const char* const DEVICE_TYPE_NAMES[] = {"INVALID", "CPU", "CUDA", "HIP", "OCL"}; +static constexpr const char* const RECO_STEP_NAMES[] = {"TPC Transformation", "TPC Sector Tracking", "TPC Track Merging and Fit", "TPC Compression", "TRD Tracking", "ITS Tracking", "TPC dEdx Computation", "TPC Cluster Finding", "TPC Decompression", "Global Refit"}; +static constexpr const char* const GENERAL_STEP_NAMES[] = {"Prepare", "QA"}; +constexpr static int32_t N_RECO_STEPS = sizeof(gpudatatypes::RECO_STEP_NAMES) / sizeof(gpudatatypes::RECO_STEP_NAMES[0]); +constexpr static int32_t N_GENERAL_STEPS = sizeof(gpudatatypes::GENERAL_STEP_NAMES) / sizeof(gpudatatypes::GENERAL_STEP_NAMES[0]); +#endif +typedef bitfield RecoStepField; +typedef bitfield InOutTypeField; +DeviceType GetDeviceType(const char* type); +} // namespace gpudatatypes + +struct GPURecoStepConfiguration { + gpudatatypes::RecoStepField steps = 0; + gpudatatypes::RecoStepField stepsGPUMask = gpudatatypes::RecoStep::AllRecoSteps; + gpudatatypes::InOutTypeField inputs = 0; + gpudatatypes::InOutTypeField outputs = 0; +}; +} // namespace o2::gpu + +#endif diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypes.h b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h similarity index 74% rename from GPU/GPUTracking/DataTypes/GPUDataTypes.h rename to GPU/GPUTracking/DataTypes/GPUDataTypesIO.h index 967d6a73914dd..76fa569a16824 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypes.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h @@ -9,18 +9,17 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUDataTypes.h +/// \file GPUDataTypesIO.h /// \author David Rohr -#ifndef GPUDATATYPES_H -#define GPUDATATYPES_H +#ifndef GPUDATATYPESIO_H +#define GPUDATATYPESIO_H #include "GPUCommonDef.h" // These are basic and non-complex data types, which will also be visible on the GPU. // Please add complex data types required on the host but not GPU to GPUHostDataTypes.h and forward-declare! #ifndef GPUCA_GPUCODE_DEVICE -#include // for bitfield below #include #endif #include "GPUTRDDef.h" @@ -85,6 +84,7 @@ class Cluster; namespace tpc { class CalibdEdxContainer; +class ORTRootSerializer; } // namespace tpc } // namespace o2 @@ -95,8 +95,7 @@ class TPCFastTransform; struct TPCPadGainCalib; struct TPCZSLinkMapping; -#include "utils/bitfield.h" - +class GPUTRDRecoParam; class GPUTPCTrack; class GPUTPCHitId; class GPUTPCGMMergedTrack; @@ -110,55 +109,10 @@ struct GPUTRDTrackletLabels; struct GPUTPCDigitsMCInput; struct GPUSettingsTF; -class GPUDataTypes +namespace gpudatatypes { - public: - // clang-format off - enum class GeometryType : uint32_t { RESERVED_GEOMETRY = 0, ALIROOT = 1, O2 = 2 }; - enum DeviceType : uint32_t { INVALID_DEVICE = 0, CPU = 1, CUDA = 2, HIP = 3, OCL = 4 }; - enum class GeneralStep { Prepare = 1, QA = 2 }; - // clang-format on - - enum class RecoStep { TPCConversion = 1, - TPCSectorTracking = 2, - TPCMerging = 4, - TPCCompression = 8, - TRDTracking = 16, - ITSTracking = 32, - TPCdEdx = 64, - TPCClusterFinding = 128, - TPCDecompression = 256, - Refit = 512, - AllRecoSteps = 0x7FFFFFFF, - NoRecoStep = 0 }; - enum class InOutType { TPCClusters = 1, - OBSOLETE = 2, - TPCMergedTracks = 4, - TPCCompressedClusters = 8, - TRDTracklets = 16, - TRDTracks = 32, - TPCRaw = 64, - ITSClusters = 128, - ITSTracks = 256 }; -#ifndef __OPENCL__ - static constexpr const char* const DEVICE_TYPE_NAMES[] = {"INVALID", "CPU", "CUDA", "HIP", "OCL"}; - static constexpr const char* const RECO_STEP_NAMES[] = {"TPC Transformation", "TPC Sector Tracking", "TPC Track Merging and Fit", "TPC Compression", "TRD Tracking", "ITS Tracking", "TPC dEdx Computation", "TPC Cluster Finding", "TPC Decompression", "Global Refit"}; - static constexpr const char* const GENERAL_STEP_NAMES[] = {"Prepare", "QA"}; - constexpr static int32_t N_RECO_STEPS = sizeof(GPUDataTypes::RECO_STEP_NAMES) / sizeof(GPUDataTypes::RECO_STEP_NAMES[0]); - constexpr static int32_t N_GENERAL_STEPS = sizeof(GPUDataTypes::GENERAL_STEP_NAMES) / sizeof(GPUDataTypes::GENERAL_STEP_NAMES[0]); -#endif - typedef bitfield RecoStepField; - typedef bitfield InOutTypeField; - static constexpr uint32_t NSECTORS = 36; - static DeviceType GetDeviceType(const char* type); -}; - -struct GPURecoStepConfiguration { - GPUDataTypes::RecoStepField steps = 0; - GPUDataTypes::RecoStepField stepsGPUMask = GPUDataTypes::RecoStep::AllRecoSteps; - GPUDataTypes::InOutTypeField inputs = 0; - GPUDataTypes::InOutTypeField outputs = 0; -}; +static constexpr uint32_t NSECTORS = 36; +} // namespace gpudatatypes template struct DefaultPtr { @@ -182,12 +136,15 @@ struct GPUCalibObjectsTemplate { // use only pointers on PODs or flat objects he typename S::type* dEdxCalibContainer = nullptr; typename S>::type* o2Propagator = nullptr; typename S::type* itsPatternDict = nullptr; + typename S::type* trdRecoParam = nullptr; + // NN clusterizer objects + typename S::type* nnClusterizerNetworks[3] = {nullptr, nullptr, nullptr}; }; typedef GPUCalibObjectsTemplate GPUCalibObjects; // NOTE: These 2 must have identical layout since they are memcopied typedef GPUCalibObjectsTemplate GPUCalibObjectsConst; struct GPUTrackingInOutZS { - static constexpr uint32_t NSECTORS = GPUDataTypes::NSECTORS; + static constexpr uint32_t NSECTORS = gpudatatypes::NSECTORS; static constexpr uint32_t NENDPOINTS = 20; struct GPUTrackingInOutZSSector { const void* const* zsPtr[NENDPOINTS]; @@ -205,7 +162,7 @@ struct GPUTrackingInOutZS { }; struct GPUTrackingInOutDigits { - static constexpr uint32_t NSECTORS = GPUDataTypes::NSECTORS; + static constexpr uint32_t NSECTORS = gpudatatypes::NSECTORS; const o2::tpc::Digit* tpcDigits[NSECTORS] = {nullptr}; size_t nTPCDigits[NSECTORS] = {0}; const GPUTPCDigitsMCInput* tpcDigitsMC = nullptr; @@ -215,7 +172,7 @@ struct GPUTrackingInOutPointers { GPUTrackingInOutPointers() = default; // TPC - static constexpr uint32_t NSECTORS = GPUDataTypes::NSECTORS; + static constexpr uint32_t NSECTORS = gpudatatypes::NSECTORS; const GPUTrackingInOutZS* tpcZS = nullptr; const GPUTrackingInOutDigits* tpcPackedDigits = nullptr; const GPUTPCClusterData* clusterData[NSECTORS] = {nullptr}; diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypesQA.h b/GPU/GPUTracking/DataTypes/GPUDataTypesQA.h new file mode 100644 index 0000000000000..6ec6de0ed4a57 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesQA.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. + +/// \file GPUDataTypesIO.h +/// \author David Rohr + +#ifndef GPUDATATYPESQA_H +#define GPUDATATYPESQA_H + +#include "GPUCommonDef.h" + +#include + +namespace o2::gpu::gpudatatypes::gpuqa +{ +enum gpuQATaskIds : int32_t { + tasksNone = 0, + taskTrackingEff = 1, + taskTrackingRes = 2, + taskTrackingResPull = 4, + taskClusterAttach = 8, + tasksAllMC = 16 - 1, + taskTrackStatistics = 16, + taskClusterCounts = 32, + taskClusterRejection = 64, + tasksAll = 128 - 1, + tasksDefault = tasksAll, + tasksDefaultPostprocess = tasksDefault & ~taskClusterCounts, + tasksAllNoQC = tasksAll & ~tasksAllMC, + tasksAutomatic = -1 +}; +} // namespace o2::gpu::gpudatatypes::gpuqa + +#endif diff --git a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h index ff8abdc1a491e..067a11817d7ac 100644 --- a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h +++ b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h @@ -43,7 +43,9 @@ struct GPUMemorySizeScalers { double tpcClustersPerPeak = 0.9; double tpcStartHitsPerHit = 0.08; double tpcTrackletsPerStartHit = 0.8; + double tpcTrackletsPerStartHitLowField = 0.85; double tpcTrackletHitsPerHit = 5; + double tpcTrackletHitsPerHitLowField = 7; double tpcSectorTracksPerHit = 0.02; double tpcSectorTrackHitsPerHit = 0.8; double tpcSectorTrackHitsPerHitWithRejection = 1.0; @@ -80,8 +82,8 @@ struct GPUMemorySizeScalers { inline size_t NTPCClusters(size_t tpcDigits, bool perSector = false) { return getValue(perSector ? tpcMaxSectorClusters : tpcMaxClusters, (conservative ? 1.0 : tpcClustersPerPeak) * NTPCPeaks(tpcDigits, perSector)); } inline size_t NTPCStartHits(size_t tpcHits) { return getValue(tpcMaxStartHits, tpcHits * tpcStartHitsPerHit); } inline size_t NTPCRowStartHits(size_t tpcHits) { return getValue(tpcMaxRowStartHits, std::max(NTPCStartHits(tpcHits) * (tpcHits < 30000000 ? 20 : 12) / GPUCA_ROW_COUNT, tpcMinRowStartHits)); } - inline size_t NTPCTracklets(size_t tpcHits) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * tpcTrackletsPerStartHit); } - inline size_t NTPCTrackletHits(size_t tpcHits) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * tpcTrackletHitsPerHit); } + inline size_t NTPCTracklets(size_t tpcHits, bool lowField) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * (lowField ? tpcTrackletsPerStartHitLowField : tpcTrackletsPerStartHit)); } + inline size_t NTPCTrackletHits(size_t tpcHits, bool lowField) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * (lowField ? tpcTrackletHitsPerHitLowField : tpcTrackletHitsPerHit)); } inline size_t NTPCSectorTracks(size_t tpcHits) { return getValue(tpcMaxSectorTracks, tpcHits * tpcSectorTracksPerHit); } inline size_t NTPCSectorTrackHits(size_t tpcHits, uint8_t withRejection = 0) { return getValue(tpcMaxSectorTrackHits, tpcHits * (withRejection ? tpcSectorTrackHitsPerHitWithRejection : tpcSectorTrackHitsPerHit)); } inline size_t NTPCMergedTracks(size_t tpcSectorTracks) { return getValue(tpcMaxMergedTracks, tpcSectorTracks * (conservative ? 1.0 : tpcMergedTrackPerSectorTrack)); } diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx similarity index 95% rename from GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx rename to GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx index 86ae8e3457019..60fdbe8042c2d 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx @@ -9,12 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUO2InterfaceConfigurableParam.cxx +/// \file GPUO2ConfigurableParam.cxx /// \author David Rohr -#include "GPUO2InterfaceConfigurableParam.h" -#include "GPUO2InterfaceConfiguration.h" -#include "GPUDataTypes.h" +#include "GPUO2ConfigurableParam.h" +#include "Interface/GPUO2InterfaceConfiguration.h" +#include "GPUDataTypesIO.h" #include "GPUConfigDump.h" using namespace o2::gpu; @@ -122,7 +122,7 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam(GPUO2InterfaceC if (obj.configReconstruction.tpc.trackReferenceX == 1000.f) { obj.configReconstruction.tpc.trackReferenceX = 83.f; } - obj.configDeviceBackend.deviceType = GPUDataTypes::GetDeviceType(global.deviceType.c_str()); + obj.configDeviceBackend.deviceType = gpudatatypes::GetDeviceType(global.deviceType.c_str()); obj.configDeviceBackend.forceDeviceType = global.forceDeviceType; return global; } diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h similarity index 87% rename from GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h rename to GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h index ebb426b7a8cfe..8bc0d98910f54 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUO2InterfaceConfigurableParam.h +/// \file GPUO2ConfigurableParam.h /// \author David Rohr // This file auto-generates a ConfigurableParam object from the GPU parameter macros. @@ -19,17 +19,10 @@ // --configKeyValues "GPU_proc.[x]=[y]" : for processing options steering GPU reconstruction like GPU device ID, debug output level, number of CPU threads. // Check GPUSettingsList.h for all options -#ifndef GPUO2INTERFACECONFIGURABLEPARAM_H -#define GPUO2INTERFACECONFIGURABLEPARAM_H - -// Some defines denoting that we are compiling for O2 -#ifndef GPUCA_TPC_GEOMETRY_O2 -#define GPUCA_TPC_GEOMETRY_O2 -#endif -#ifndef GPUCA_O2_INTERFACE -#define GPUCA_O2_INTERFACE -#endif +#ifndef GPUO2CONFIGURABLEPARAM_H +#define GPUO2CONFIGURABLEPARAM_H +#include "GPUO2ExternalUser.h" #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" #include "GPUSettings.h" @@ -50,7 +43,7 @@ #define AddSubConfig(name, instance) #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ struct GPUCA_M_CAT(GPUConfigurableParam, name) : public o2::conf::ConfigurableParamHelper { \ - O2ParamDef(GPUCA_M_CAT(GPUConfigurableParam, name), GPUCA_M_STR(GPUCA_M_CAT(GPU_, o2prefix))) public: + O2ParamDef(GPUCA_M_CAT(GPUConfigurableParam, name), GPUCA_M_STR(GPUCA_M_CAT(GPU_, o2prefix))) public: #define BeginHiddenConfig(name, instance) struct GPUCA_M_CAT(GPUConfigurableParam, name) { #define EndConfig() \ } \ diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx b/GPU/GPUTracking/DataTypes/GPUO2ExternalUser.h similarity index 64% rename from Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx rename to GPU/GPUTracking/DataTypes/GPUO2ExternalUser.h index 4f3a8a44b0391..1e5261e0065a6 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx +++ b/GPU/GPUTracking/DataTypes/GPUO2ExternalUser.h @@ -9,14 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITS3Reconstruction/FastMultEstConfig.h" -#include "TRandom.h" +/// \file GPUO2ExternalUser.h +/// \author David Rohr -O2ParamImpl(o2::its::FastMultEstConfig); +#ifndef GPUO2EXTERNALUSER_H +#define GPUO2EXTERNALUSER_H -using namespace o2::its; +// Some defines denoting that we are compiling for O2 +#ifndef GPUCA_TPC_GEOMETRY_O2 +#define GPUCA_TPC_GEOMETRY_O2 +#endif +#ifndef GPUCA_O2_INTERFACE +#define GPUCA_O2_INTERFACE +#endif -bool FastMultEstConfig::isPassingRandomRejection() const -{ - return (cutRandomFraction <= 0. || gRandom->Rndm() > cutRandomFraction) ? true : false; -} +#endif diff --git a/GPU/GPUTracking/DataTypes/GPUSettings.h b/GPU/GPUTracking/DataTypes/GPUSettings.h index 9e3a3e9bd6ce8..34b378b046aec 100644 --- a/GPU/GPUTracking/DataTypes/GPUSettings.h +++ b/GPU/GPUTracking/DataTypes/GPUSettings.h @@ -16,7 +16,7 @@ #define GPUSETTINGS_H #include "GPUCommonDef.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" #include "GPUTPCGMMergedTrackHit.h" #ifndef GPUCA_GPUCODE_DEVICE #include @@ -73,7 +73,7 @@ struct GPUSettingsTF { // Settings defining the setup of the GPUReconstruction processing (basically selecting the device / class instance) struct GPUSettingsDeviceBackend { - uint32_t deviceType = GPUDataTypes::DeviceType::CPU; // Device type, shall use GPUDataTypes::DEVICE_TYPE constants, e.g. CPU / CUDA + uint32_t deviceType = gpudatatypes::DeviceType::CPU; // Device type, shall use gpudatatypes::DEVICE_TYPE constants, e.g. CPU / CUDA uint8_t forceDeviceType = 1; // Fail if device initialization fails, otherwise falls back to CPU GPUReconstruction* master = nullptr; // GPUReconstruction master object }; diff --git a/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h b/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h index 9ad83bff363ac..13cec6c752fbe 100644 --- a/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h +++ b/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h @@ -96,6 +96,7 @@ class GPUTPCGeometry // TODO: Make values constexpr GPUd() static constexpr int32_t EndIROC() { return 63; } GPUd() static constexpr int32_t EndOROC1() { return 97; } GPUd() static constexpr int32_t EndOROC2() { return 127; } + GPUd() static constexpr int32_t MaxNPadsPerRow() { return 138; } #else GPUd() static constexpr int32_t GetRegion(int32_t row) { return (row < 63 ? 0 : row < 63 + 64 ? 1 : 2); } GPUd() static constexpr int32_t GetRegionRows(int32_t region) { return 0; } // dummy @@ -104,6 +105,7 @@ class GPUTPCGeometry // TODO: Make values constexpr GPUd() static constexpr int32_t EndIROC() { return 63; } GPUd() static constexpr int32_t EndOROC1() { return 63 + 64; } GPUd() static constexpr int32_t EndOROC2() { return GPUCA_ROW_COUNT; } + GPUd() static constexpr int32_t MaxNPadsPerRow() { return 140; } #endif GPUd() static constexpr float TPCLength() { return 250.f - 0.275f; } diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx new file mode 100644 index 0000000000000..70b445f7befc0 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.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 GPUTRDRecoParam.cxx +/// \brief Error parameterizations and helper functions for TRD reconstruction +/// \author Ole Schmidt + +#include "GPUSettings.h" +#include "GPUTRDRecoParam.h" +#include "GPUCommonLogger.h" +#include "GPUCommonMath.h" + +using namespace o2::gpu; + +// error parameterizations taken from http://cds.cern.ch/record/2724259 Appendix A +void GPUTRDRecoParam::init(float bz, const GPUSettingsRec* rec) +{ + float resRPhiIdeal2 = rec ? rec->trd.trkltResRPhiIdeal * rec->trd.trkltResRPhiIdeal : 1.6e-3f; + + if (CAMath::Abs(CAMath::Abs(bz) - 2) < 0.1) { + if (bz > 0) { + // magnetic field +0.2 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = -1.43e-2f; + mRPhiC2 = 4.55e-2f; + + mDyA2 = 1.225e-3f; + mDyB = -9.8e-3f; + mDyC2 = 3.88e-2f; + + mAngleToDyA = -0.1f; + mAngleToDyB = 1.89f; + mAngleToDyC = -0.4f; + } else { + // magnetic field -0.2 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = 1.43e-2f; + mRPhiC2 = 4.55e-2f; + + mDyA2 = 1.225e-3f; + mDyB = 9.8e-3f; + mDyC2 = 3.88e-2f; + + mAngleToDyA = 0.1f; + mAngleToDyB = 1.89f; + mAngleToDyC = 0.4f; + } + } else if (CAMath::Abs(CAMath::Abs(bz) - 5) < 0.1) { + if (bz > 0) { + // magnetic field +0.5 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = 0.125f; + mRPhiC2 = 0.0961f; + + mDyA2 = 1.681e-3f; + mDyB = 0.15f; + mDyC2 = 0.1849f; + + mAngleToDyA = 0.13f; + mAngleToDyB = 2.43f; + mAngleToDyC = -0.58f; + } else { + // magnetic field -0.5 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = -0.14f; + mRPhiC2 = 0.1156f; + + mDyA2 = 2.209e-3f; + mDyB = -0.15f; + mDyC2 = 0.2025f; + + mAngleToDyA = -0.15f; + mAngleToDyB = 2.34f; + mAngleToDyC = 0.56f; + } + } else { + LOGP(warning, "No error parameterization available for Bz= {}. Keeping default value (sigma_y = const. = 1cm)", bz); + } + LOGP(info, "Loaded parameterizations for Bz={}: PhiRes:[{},{},{}] DyRes:[{},{},{}] Angle2Dy:[{},{},{}]", + bz, mRPhiA2, mRPhiB, mRPhiC2, mDyA2, mDyB, mDyC2, mAngleToDyA, mAngleToDyB, mAngleToDyC); +} + +void GPUTRDRecoParam::recalcTrkltCov(const float tilt, const float snp, const float rowSize, float* 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/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h new file mode 100644 index 0000000000000..ad0285487d3c3 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h @@ -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 RecoParam.h +/// \brief Error parameterizations and helper functions for TRD reconstruction +/// \author Ole Schmidt + +#ifndef O2_GPU_TRD_RECOPARAM_H +#define O2_GPU_TRD_RECOPARAM_H + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" + +namespace o2 +{ +namespace gpu +{ +struct GPUSettingsRec; + +class GPUTRDRecoParam +{ + public: + GPUTRDRecoParam() = default; + GPUTRDRecoParam(const GPUTRDRecoParam&) = default; + ~GPUTRDRecoParam() = default; + + /// Load parameterization for given magnetic field + void init(float bz, const GPUSettingsRec* rec = nullptr); + +#if !defined(GPUCA_GPUCODE_DEVICE) + /// Recalculate tracklet covariance based on phi angle of related track + GPUd() void recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array& cov) const + { + recalcTrkltCov(tilt, snp, rowSize, cov.data()); + } +#endif + GPUd() void recalcTrkltCov(const float tilt, const float snp, const float rowSize, float* 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) + /// 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 + GPUd() float getRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mRPhiB) * (snp - mRPhiB)); } + GPUd() float getDyRes(float snp) const { return mDyA2 + mDyC2 * (snp - mDyB) * (snp - mDyB); } // // a^2 + c^2 * (snp - b)^2 + GPUd() float convertAngleToDy(float snp) const { return mAngleToDyA + mAngleToDyB * snp + mAngleToDyC * snp * snp; } // a + b*snp + c*snp^2 is more accurate than sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) + + /// Get tracklet z correction coefficient for track-eta based corraction + GPUd() float getZCorrCoeffNRC() const { return mZCorrCoefNRC; } + + private: + // tracklet error parameterization depends on the magnetic field + // rphi + float mRPhiA2{1.f}; ///< parameterization for tracklet position resolution + float mRPhiB{0.f}; ///< parameterization for tracklet position resolution + float mRPhiC2{0.f}; ///< parameterization for tracklet position resolution + // angle + float mDyA2{1.225e-3f}; ///< parameterization for tracklet angular resolution + float mDyB{0.f}; ///< parameterization for tracklet angular resolution + float mDyC2{0.f}; ///< parameterization for tracklet angular resolution + // angle to Dy + float mAngleToDyA; // parameterization for conversion track angle -> tracklet deflection + float mAngleToDyB; // parameterization for conversion track angle -> tracklet deflection + float mAngleToDyC; // parameterization for conversion track angle -> tracklet deflection + + float mZCorrCoefNRC{1.4f}; ///< tracklet z-position depends linearly on track dip angle + + ClassDefNV(GPUTRDRecoParam, 2); +}; + +} // namespace gpu +} // namespace o2 + +#endif // O2_GPU_TRD_RECOPARAM_H diff --git a/Detectors/TPC/workflow/src/time-series-reader.cxx b/GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx similarity index 57% rename from Detectors/TPC/workflow/src/time-series-reader.cxx rename to GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx index ccedbdf4f9599..82a8be1fdfec8 100644 --- a/Detectors/TPC/workflow/src/time-series-reader.cxx +++ b/GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx @@ -9,17 +9,17 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "TPCWorkflow/TPCTimeSeriesReaderSpec.h" -#include "CommonUtils/ConfigurableParam.h" -#include "Framework/ConfigParamSpec.h" +/// \file ORTRootSerializer.cxx +/// \author Christian Sonnabend -using namespace o2::framework; +#include "ORTRootSerializer.h" +#include -#include "Framework/runDataProcessing.h" +using namespace o2::tpc; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +/// Initialize the serialization from a char* buffer containing the model +void ORTRootSerializer::setOnnxModel(const char* onnxModel, uint32_t size) { - WorkflowSpec wf; - wf.emplace_back(o2::tpc::getTPCTimeSeriesReaderSpec()); - return wf; + mModelBuffer.resize(size); + std::memcpy(mModelBuffer.data(), onnxModel, size); } diff --git a/GPU/GPUTracking/DataTypes/ORTRootSerializer.h b/GPU/GPUTracking/DataTypes/ORTRootSerializer.h new file mode 100644 index 0000000000000..24009d4435a96 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/ORTRootSerializer.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. + +/// \file ORTRootSerializer.h +/// \brief Class to serialize ONNX objects for ROOT snapshots of CCDB objects at runtime +/// \author Christian Sonnabend + +#ifndef ALICEO2_TPC_ORTROOTSERIALIZER_H_ +#define ALICEO2_TPC_ORTROOTSERIALIZER_H_ + +#include "GPUCommonRtypes.h" +#include +#include + +namespace o2::tpc +{ + +class ORTRootSerializer +{ + public: + ORTRootSerializer() = default; + ~ORTRootSerializer() = default; + + void setOnnxModel(const char* onnxModel, uint32_t size); + const char* getONNXModel() const { return mModelBuffer.data(); } + uint32_t getONNXModelSize() const { return static_cast(mModelBuffer.size()); } + + private: + std::vector mModelBuffer; ///< buffer for serialization + ClassDefNV(ORTRootSerializer, 1); +}; + +} // namespace o2::tpc + +#endif // ALICEO2_TPC_ORTROOTSERIALIZER_H_ diff --git a/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h b/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h index e5a2c8eb75bcb..0644e533e9d64 100644 --- a/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h +++ b/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h @@ -23,7 +23,7 @@ #include "GPUCommonDef.h" #if !defined(GPUCA_STANDALONE) && !defined(GPUCA_O2_LIB) && !defined(GPUCA_O2_INTERFACE) - #error You are using the CA GPU tracking without defining the build type (O2/Standalone). If you are running an O2 ROOT macro, please include GPUO2Interface.h first! + #error You are using the CA GPU tracking without defining the build type (O2/Standalone). If you are running an O2 ROOT macro, please include GPUO2ExternalUser.h first! #endif #if (defined(GPUCA_O2_LIB) && defined(GPUCA_STANDALONE)) @@ -35,8 +35,6 @@ #define GPUCA_MERGER_MAX_TRACK_CLUSTERS 1024 // Maximum number of clusters a track may have after merging #define GPUCA_MAXN 40 // Maximum number of neighbor hits to consider in one row in neightbors finder -#define GPUCA_MIN_TRACK_PTB5_DEFAULT 0.010f // Default setting for minimum track Pt at some places (at B=0.5T) -#define GPUCA_MIN_TRACK_PTB5_REJECT_DEFAULT 0.050f // Default setting for Pt (at B=0.5T) where tracks are rejected #define GPUCA_MAX_SIN_PHI_LOW 0.99f // Limits for maximum sin phi during fit #define GPUCA_MAX_SIN_PHI 0.999f // Must be preprocessor define because c++ pre 11 cannot use static constexpr for initializes diff --git a/GPU/GPUTracking/Definitions/GPUDefMacros.h b/GPU/GPUTracking/Definitions/GPUDefMacros.h index caf2d1670f84e..ea62d7f34edb0 100644 --- a/GPU/GPUTracking/Definitions/GPUDefMacros.h +++ b/GPU/GPUTracking/Definitions/GPUDefMacros.h @@ -50,5 +50,7 @@ #define GPUCA_UNROLL(...) #endif +#define GPUCA_CEIL_INT_DIV(a, b) (((a) + (b) - 1) / (b)) + #endif // clang-format on diff --git a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h deleted file mode 100644 index 29aa3808506dc..0000000000000 --- a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file GPUDefParametersDefaults.h -/// \author David Rohr - -// This file contains compile-time constants affecting the GPU performance. - -#if !defined(GPUDEFPARAMETERSDEFAULTS_H) -#define GPUDEFPARAMETERSDEFAULTS_H -// clang-format off - -// Launch bound definition, 3 optional parameters: maxThreads per block, minBlocks per multiprocessor, force number of blocks (not passed to compiler as launch bounds) - -// GPU Run Configuration -#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection. - // GPU-architecture-dependent default settings - #if defined(GPUCA_GPUTYPE_MI2xx) - #define GPUCA_WARP_SIZE 64 - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #define GPUCA_LB_GPUTPCCreateTrackingData 256 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 1 - #define GPUCA_LB_GPUTPCStartHitsFinder 1024 - #define GPUCA_LB_GPUTPCTrackletConstructor 512, 2 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 3 - #define GPUCA_LB_GPUTPCNeighboursFinder 1024, 1 - #define GPUCA_LB_GPUTPCNeighboursCleaner 896 - #define GPUCA_LB_GPUTPCExtrapolationTracking 256 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 4 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 128, 1 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 64, 12 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 512 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 512 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 512 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 512 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 512 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 64, 2 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 128, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 64, 2 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #define GPUCA_LB_GPUTPCCFPeakFinder 512 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #define GPUCA_LB_GPUTPCCFDeconvolution 512 - #define GPUCA_LB_GPUTPCCFClusterizer 448 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 5 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 20 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #elif defined(GPUCA_GPUTYPE_VEGA) - #define GPUCA_WARP_SIZE 64 - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #define GPUCA_LB_GPUTPCCreateTrackingData 128 - #define GPUCA_LB_GPUTPCStartHitsSorter 1024, 2 - #define GPUCA_LB_GPUTPCStartHitsFinder 1024 - #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 - #define GPUCA_LB_GPUTPCTrackletSelector 256, 8 - #define GPUCA_LB_GPUTPCNeighboursFinder 1024, 1 - #define GPUCA_LB_GPUTPCNeighboursCleaner 896 - #define GPUCA_LB_GPUTPCExtrapolationTracking 256 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 4 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 1 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 4, 200 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 256 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 512 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 192, 2 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 128, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 64, 2 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #define GPUCA_LB_GPUTPCCFPeakFinder 512 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #define GPUCA_LB_GPUTPCCFDeconvolution 512 - #define GPUCA_LB_GPUTPCCFClusterizer 512 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 5 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 20 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #elif defined(GPUCA_GPUTYPE_AMPERE) - #define GPUCA_WARP_SIZE 32 - #define GPUCA_THREAD_COUNT_DEFAULT 512 - #define GPUCA_LB_GPUTPCCreateTrackingData 384 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 1 - #define GPUCA_LB_GPUTPCStartHitsFinder 512 - #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 // best single-kernel: 128, 4 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 3 // best single-kernel: 128, 4 - #define GPUCA_LB_GPUTPCNeighboursFinder 640, 1 // best single-kernel: 768, 1 - #define GPUCA_LB_GPUTPCNeighboursCleaner 512 - #define GPUCA_LB_GPUTPCExtrapolationTracking 128, 4 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 10 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 4 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 64, 12 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 32, 6 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256, 4 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256, 2 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 192 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 64, 2 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 256, 2 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 64, 2 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 3 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 32, 1 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 32, 1 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64,8 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 448 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 448 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 448 - #define GPUCA_LB_GPUTPCCFPeakFinder 128 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 448 - #define GPUCA_LB_GPUTPCCFDeconvolution 384 - #define GPUCA_LB_GPUTPCCFClusterizer 448 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 20 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #elif defined(GPUCA_GPUTYPE_TURING) - #define GPUCA_WARP_SIZE 32 - #define GPUCA_THREAD_COUNT_DEFAULT 512 - #define GPUCA_LB_GPUTPCCreateTrackingData 256 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 1 - #define GPUCA_LB_GPUTPCStartHitsFinder 512 - #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 3 - #define GPUCA_LB_GPUTPCNeighboursFinder 640, 1 - #define GPUCA_LB_GPUTPCNeighboursCleaner 512 - #define GPUCA_LB_GPUTPCExtrapolationTracking 192, 2 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 8 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 32, 8 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 128, 4 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 64, 5 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256, 4 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256, 2 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 192 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 128, 2 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 128 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 32, 1 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 32, 1 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 20 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #elif defined(GPUCA_GPUTYPE_OPENCL) - #else - #error GPU TYPE NOT SET - #endif - - // Default settings for GPU, if not already set for selected GPU type - #ifndef GPUCA_WARP_SIZE - #define GPUCA_WARP_SIZE 32 - #endif - #ifndef GPUCA_THREAD_COUNT_DEFAULT - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #endif - #ifndef GPUCA_LB_GPUTPCCreateTrackingData - #define GPUCA_LB_GPUTPCCreateTrackingData 256 - #endif - #ifndef GPUCA_LB_GPUTPCTrackletConstructor - #define GPUCA_LB_GPUTPCTrackletConstructor 256 - #endif - #ifndef GPUCA_LB_GPUTPCTrackletSelector - #define GPUCA_LB_GPUTPCTrackletSelector 256 - #endif - #ifndef GPUCA_LB_GPUTPCNeighboursFinder - #define GPUCA_LB_GPUTPCNeighboursFinder 256 - #endif - #ifndef GPUCA_LB_GPUTPCNeighboursCleaner - #define GPUCA_LB_GPUTPCNeighboursCleaner 256 - #endif - #ifndef GPUCA_LB_GPUTPCExtrapolationTracking - #define GPUCA_LB_GPUTPCExtrapolationTracking 256 - #endif - #ifndef GPUCA_LB_GPUTRDTrackerKernels_gpuVersion - #define GPUCA_LB_GPUTRDTrackerKernels_gpuVersion 512 - #endif - #ifndef GPUCA_LB_GPUTPCCreateOccupancyMap_fill - #define GPUCA_LB_GPUTPCCreateOccupancyMap_fill 256 - #endif - #ifndef GPUCA_LB_GPUTPCCreateOccupancyMap_fold - #define GPUCA_LB_GPUTPCCreateOccupancyMap_fold 256 - #endif - #ifndef GPUCA_LB_GPUTRDTrackerKernels_o2Version - #define GPUCA_LB_GPUTRDTrackerKernels_o2Version 512 - #endif - #ifndef GPUCA_LB_GPUTPCCompressionKernels_step0attached - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 256 - #endif - #ifndef GPUCA_LB_GPUTPCCompressionKernels_step1unattached - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionKernels_step0attached - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionKernels_step1unattached - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionUtilKernels_sortPerSectorRow - #define GPUCA_LB_GPUTPCDecompressionUtilKernels_sortPerSectorRow 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionUtilKernels_countFilteredClusters - #define GPUCA_LB_GPUTPCDecompressionUtilKernels_countFilteredClusters 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionUtilKernels_storeFilteredClusters - #define GPUCA_LB_GPUTPCDecompressionUtilKernels_storeFilteredClusters 256 - #endif - #ifndef GPUCA_LB_GPUTPCCFDecodeZS - #define GPUCA_LB_GPUTPCCFDecodeZS 128, 4 - #endif - #ifndef GPUCA_LB_GPUTPCCFDecodeZSLink - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #endif - #ifndef GPUCA_LB_GPUTPCCFDecodeZSDenseLink - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #endif - #ifndef GPUCA_LB_GPUTPCCFGather - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #endif - #ifndef GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerTrackFit - #define GPUCA_LB_GPUTPCGMMergerTrackFit 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFollowLoopers - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerSectorRefit - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerUnpackResetIds - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerUnpackGlobal - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step0 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step1 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step2 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step3 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step4 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerClearLinks - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeCE - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerCollect - #define GPUCA_LB_GPUTPCGMMergerCollect 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerSortTracksPrepare - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step0 - #define GPUCA_LB_GPUTPCGMMergerFinalize_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step1 - #define GPUCA_LB_GPUTPCGMMergerFinalize_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step2 - #define GPUCA_LB_GPUTPCGMMergerFinalize_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers_step0 - #define GPUCA_LB_GPUTPCGMMergerMergeLoopers_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers_step1 - #define GPUCA_LB_GPUTPCGMMergerMergeLoopers_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers_step2 - #define GPUCA_LB_GPUTPCGMMergerMergeLoopers_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMO2Output_prepare - #define GPUCA_LB_GPUTPCGMO2Output_prepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMO2Output_output - #define GPUCA_LB_GPUTPCGMO2Output_output 256 - #endif - #ifndef GPUCA_LB_GPUTPCStartHitsFinder - #define GPUCA_LB_GPUTPCStartHitsFinder 256 - #endif - #ifndef GPUCA_LB_GPUTPCStartHitsSorter - #define GPUCA_LB_GPUTPCStartHitsSorter 256 - #endif - #ifndef GPUCA_LB_GPUTPCCFCheckPadBaseline - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64 - #endif - #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFPeakFinder - #define GPUCA_LB_GPUTPCCFPeakFinder 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFNoiseSuppression - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFDeconvolution - #define GPUCA_LB_GPUTPCCFDeconvolution 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFClusterizer - #define GPUCA_LB_GPUTPCCFClusterizer 512 - #endif - #ifndef GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels 512 - #endif - #ifndef GPUCA_LB_GPUTrackingRefitKernel_mode0asGPU - #define GPUCA_LB_GPUTrackingRefitKernel_mode0asGPU 256 - #endif - #ifndef GPUCA_LB_GPUTrackingRefitKernel_mode1asTrackParCov - #define GPUCA_LB_GPUTrackingRefitKernel_mode1asTrackParCov 256 - #endif - #ifndef GPUCA_LB_GPUMemClean16 - #define GPUCA_LB_GPUMemClean16 GPUCA_THREAD_COUNT_DEFAULT, 1 - #endif - #ifndef GPUCA_LB_GPUitoa - #define GPUCA_LB_GPUitoa GPUCA_THREAD_COUNT_DEFAULT, 1 - #endif - // These kernel launch-bounds are derrived from one of the constants set above - #define GPUCA_LB_GPUTPCCFNoiseSuppression_noiseSuppression GPUCA_LB_GPUTPCCFNoiseSuppression - #define GPUCA_LB_GPUTPCCFNoiseSuppression_updatePeaks GPUCA_LB_GPUTPCCFNoiseSuppression - - #define GPUCA_LB_GPUTPCNNClusterizerKernels_runCfClusterizer GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_fillInputNNCPU GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_fillInputNNGPU GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_determineClass1Labels GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_determineClass2Labels GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_publishClass1Regression GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_publishClass2Regression GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_publishDeconvolutionFlags GPUCA_LB_GPUTPCNNClusterizerKernels - - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanStart GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanUp GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanTop GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanDown GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_compactDigits GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCompressionGatherKernels_unbuffered GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered32 GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered64 GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered128 GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_multiBlock GPUCA_LB_COMPRESSION_GATHER - - // Defaults for non-LB parameters - #ifndef GPUCA_PAR_SORT_STARTHITS - #define GPUCA_PAR_SORT_STARTHITS 1 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 6 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 4 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 1 - #endif - #ifndef GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 12 - #endif - #ifndef GPUCA_PAR_ALTERNATE_BORDER_SORT - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 0 - #endif - #ifndef GPUCA_PAR_SORT_BEFORE_FIT - #define GPUCA_PAR_SORT_BEFORE_FIT 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_KERNEL - #define GPUCA_PAR_COMP_GATHER_KERNEL 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_MODE - #define GPUCA_PAR_COMP_GATHER_MODE 2 - #endif - #ifndef GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE 512 - #endif -#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) - -#ifndef GPUCA_GPUCODE_GENRTC - // Defaults (also for CPU) for non-LB parameters - #ifndef GPUCA_PAR_SORT_STARTHITS - #define GPUCA_PAR_SORT_STARTHITS 0 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 0 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 0 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 0 - #endif - #ifndef GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 0 - #endif - #ifndef GPUCA_PAR_ALTERNATE_BORDER_SORT - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 0 - #endif - #ifndef GPUCA_PAR_SORT_BEFORE_FIT - #define GPUCA_PAR_SORT_BEFORE_FIT 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_KERNEL - #define GPUCA_PAR_COMP_GATHER_KERNEL 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_MODE - #define GPUCA_PAR_COMP_GATHER_MODE 0 - #endif - #ifndef GPUCA_PAR_NO_ATOMIC_PRECHECK - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 0 - #endif - #ifndef GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE 0 - #endif - #ifndef GPUCA_PAR_DEDX_STORAGE_TYPE - #define GPUCA_PAR_DEDX_STORAGE_TYPE float - #endif - #ifndef GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE float - #endif -#endif // GPUCA_GPUCODE_GENRTC - -// clang-format on -#endif // GPUDEFPARAMETERSDEFAULTS_H diff --git a/GPU/GPUTracking/Definitions/GPUDefParametersLoad.template.inc b/GPU/GPUTracking/Definitions/GPUDefParametersLoad.template.inc index ac71adc6232a6..8b7a79a9e48bf 100644 --- a/GPU/GPUTracking/Definitions/GPUDefParametersLoad.template.inc +++ b/GPU/GPUTracking/Definitions/GPUDefParametersLoad.template.inc @@ -39,23 +39,23 @@ static GPUDefParameters GPUDefParametersLoad() }; } -#define GPUCA_EXPORT_KERNEL_LB(name) \ - if (par.par_LB_maxThreads[i] > 0) { \ - o << "#define GPUCA_LB_" GPUCA_M_STR(name) " " << par.par_LB_maxThreads[i]; \ - if (par.par_LB_minBlocks[i] > 0) { \ - o << ", " << par.par_LB_minBlocks[i]; \ - } \ - if (!forRTC && par.par_LB_forceBlocks[i] > 0) { \ - o << ", " << par.par_LB_forceBlocks[i]; \ - } \ - o << "\n"; \ - } \ +#define GPUCA_EXPORT_KERNEL_LB(name) \ + if (par.par_LB_maxThreads[i] > 0) { \ + o << "#define GPUCA_LB_" GPUCA_M_STR(name) " " << par.par_LB_maxThreads[i]; \ + if (par.par_LB_minBlocks[i] > 0) { \ + o << ", " << GPUCA_CEIL_INT_DIV(par.par_LB_maxThreads[i] * par.par_LB_minBlocks[i], (minBlockFactor ? minBlockFactor : par.par_LB_maxThreads[i])); \ + } \ + if (!forRTC && par.par_LB_forceBlocks[i] > 0) { \ + o << ", " << par.par_LB_forceBlocks[i]; \ + } \ + o << "\n"; \ + } \ i++; #define GPUCA_EXPORT_KERNEL_PARAM(name) \ o << "#define GPUCA_PAR_" GPUCA_M_STR(name) " " << GPUCA_M_CAT(par.par_, name) << "\n"; -static std::string GPUDefParametersExport(const GPUDefParameters& par, bool forRTC) +static std::string GPUDefParametersExport(const GPUDefParameters& par, bool forRTC, int32_t minBlockFactor = 0) { std::stringstream o; // clang-format off int32_t i = 0; diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 30477d67fdc4f..c61056466929e 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -19,7 +19,7 @@ // Create plain-C struct for GPU code. // Create static constexpr with default values for GPU run time compilation -#include "GPUDefConstantsAndSettings.h" +#include "GPUCommonDef.h" #ifndef GPUSETTINGS_H #error Please include GPUSettings.h! #endif @@ -39,7 +39,7 @@ BeginNamespace(gpu) // Reconstruction parameters for TPC, no bool in here !!! BeginSubConfig(GPUSettingsRecTPC, tpc, configStandalone.rec, "RECTPC", 0, "Reconstruction settings", rec_tpc) -AddOptionRTC(rejectQPtB5, float, 1.f / GPUCA_MIN_TRACK_PTB5_REJECT_DEFAULT, "", 0, "QPt threshold to reject clusters of TPC tracks (Inverse Pt, scaled to B=0.5T!!!)") +AddOptionRTC(rejectQPtB5, float, 1.f / 0.050f, "", 0, "QPt threshold to reject clusters of TPC tracks (Inverse Pt, scaled to B=0.5T!!!)") AddOptionRTC(hitPickUpFactor, float, 1.f, "", 0, "multiplier for the combined cluster+track error during track following") AddOptionRTC(hitSearchArea2, float, 2.f, "", 0, "square of maximum search road of hits during seeding") AddOptionRTC(neighboursSearchArea, float, 3.f, "", 0, "area in cm for the search of neighbours, for z only used if searchWindowDZDR = 0") @@ -68,8 +68,12 @@ AddOptionRTC(minNClustersFinalTrack, int32_t, -1, "", 0, "required min number of AddOptionRTC(searchWindowDZDR, float, 2.5f, "", 0, "Use DZDR window for seeding instead of neighboursSearchArea") AddOptionRTC(trackReferenceX, float, 1000.f, "", 0, "Transport all tracks to this X after tracking (disabled if > 500, auto = 1000)") AddOptionRTC(zsThreshold, float, 2.0f, "", 0, "Zero-Suppression threshold") -AddOptionRTC(tubeChi2, float, 5.f * 5.f, "", 0, "Max chi2 to mark cluster adjacent to track") -AddOptionRTC(tubeMaxSize2, float, 2.5f * 2.5f, "", 0, "Square of max tube size (normally derrived from tpcTubeChi2)") +AddOptionRTC(tubeProtectSigma2, float, 4.f * 4.f, "", 0, "Max sigma2 to mark adjacent cluster for protection") +AddOptionRTC(tubeProtectMaxSize2, float, 2.f * 2.f, "", 0, "Square of max tube size (if smaller than tubeProtectChi2)") +AddOptionRTC(tubeProtectMinSize2, float, 0.5f * 0.5f, "", 0, "Square of min tube size (if larger than tubeProtectChi2)") +AddOptionRTC(tubeRemoveSigma2, float, 1.25f * 1.25f, "", 0, "Max sigma2 to mark adjacent cluster for removal") +AddOptionRTC(tubeRemoveMaxSize2, float, 2.5f * 2.5f, "", 0, "Square of max tube size (if smaller than tubeRejectChi2)") +AddOptionRTC(tubeExtraProtectMinOccupancy, uint32_t, 1500, "", 0, "Increase Protection, decrease removal by factor 2, when above this lokal occupancy / rowx") AddOptionRTC(clustersShiftTimebins, float, 0, "", 0, "Shift of TPC clusters (applied during CTF cluster decoding)") AddOptionRTC(clustersShiftTimebinsClusterizer, float, 0, "", 0, "Shift of TPC clusters (applied during CTF clusterization)") AddOptionRTC(clustersEdgeFixDistance, float, 0.f, "", 0, "If >0, revert cluster.flag edge bit distance to edge exceeds this parameter (fixed during CTF decoding)") @@ -159,6 +163,9 @@ AddOptionRTC(dEdxClusterRejectionFlagMask, int8_t, o2::gpu::GPUTPCGMMergedTrackH AddOptionRTC(dEdxClusterRejectionFlagMaskAlt, int8_t, o2::gpu::GPUTPCGMMergedTrackHit::flagEdge, "", 0, "OR mask of TPC flags that will reject the cluster in alternative dEdx") AddOptionRTC(rejectEdgeClustersInSeeding, int8_t, 0, "", 0, "Reject edge clusters based on uncorrected track Y during seeding") AddOptionRTC(rejectEdgeClustersInTrackFit, int8_t, 0, "", 0, "Reject edge clusters based on uncorrected track Y during track fit") +AddOptionRTC(tubeExtraProtectMinRow, uint8_t, 20, "", 0, "Increase Protection, decrease removal by factor 2, when below this row") +AddOptionRTC(tubeExtraProtectEdgePads, uint8_t, 2, "", 0, "Increase Protection, decrease removal by factor 2, when on this number of pads from the edge") + AddOptionArray(PID_remap, int8_t, 9, (0, 1, 2, 3, 4, 5, 6, 7, 8), "", 0, "Remap Ipid to PID_reamp[Ipid] (no remap if<0)") // BUG: CUDA cannot yet hand AddOptionArrayRTC AddHelp("help", 'h') EndConfig() @@ -192,7 +199,7 @@ EndConfig() // Global reconstruction parameters, no bool in here !!! BeginSubConfig(GPUSettingsRec, rec, configStandalone, "REC", 0, "Reconstruction settings", rec) -AddOptionRTC(maxTrackQPtB5, float, 1.f / GPUCA_MIN_TRACK_PTB5_DEFAULT, "", 0, "required max Q/Pt (==min Pt) of tracks") +AddOptionRTC(maxTrackQPtB5, float, 1.f / 0.010f, "", 0, "required max Q/Pt (==min Pt) of tracks") AddOptionRTC(fwdTPCDigitsAsClusters, uint8_t, 0, "", 0, "Forward TPC digits as clusters (if they pass the ZS threshold)") AddOptionRTC(bz0Pt10MeV, uint8_t, 60, "", 0, "Nominal Pt to set when bz = 0 (in 10 MeV)") AddOptionRTC(fitInProjections, int8_t, -1, "", 0, "Fit in projection, -1 to enable full fit for all but passes but the first one") @@ -216,6 +223,7 @@ AddOption(optSpecialCode, int8_t, -1, "", 0, "Insert GPUCA_RTC_SPECIAL_CODE spec AddOption(deterministic, bool, false, "", 0, "Compile RTC in deterministic mode, with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define") AddOption(compilePerKernel, bool, true, "", 0, "Run one RTC compilation per kernel") AddOption(enable, bool, false, "", 0, "Use RTC to optimize GPU code") +AddOption(overrideWarpSize, int32_t, -1, "", 0, "Override the warp size to be used for RTC") AddHelp("help", 'h') EndConfig() @@ -269,22 +277,22 @@ AddOption(nnClusterizerBoundaryFillValue, int, -1, "", 0, "Fill value for the bo AddOption(nnClusterizerApplyNoiseSuppression, int, 1, "", 0, "Applies the NoiseSuppression kernel before the digits to the network are filled") AddOption(nnClusterizerSetDeconvolutionFlags, int, 1, "", 0, "Runs the deconvolution kernel without overwriting the charge in order to make cluster-to-track attachment identical to heuristic CF") AddOption(nnClassificationPath, std::string, "network_class.onnx", "", 0, "The classification network path") -AddOption(nnClassThreshold, float, 0.5, "", 0, "The cutoff at which clusters will be accepted / rejected.") AddOption(nnRegressionPath, std::string, "network_reg.onnx", "", 0, "The regression network path") +AddOption(nnClassThreshold, float, 0.5, "", 0, "The cutoff at which clusters will be accepted / rejected.") AddOption(nnSigmoidTrafoClassThreshold, int, 1, "", 0, "If true (default), then the classification threshold is transformed by an inverse sigmoid function. This depends on how the network was trained (with a sigmoid as acitvation function in the last layer or not).") AddOption(nnEvalMode, std::string, "c1:r1", "", 0, "Concatention of modes, e.g. c1:r1 (classification class 1, regression class 1)") AddOption(nnClusterizerUseClassification, int, 1, "", 0, "If 1, the classification output of the network is used to select clusters, else only the regression output is used and no clusters are rejected by classification") AddOption(nnClusterizerForceGpuInputFill, int, 0, "", 0, "Forces to use the fillInputNNGPU function") // CCDB AddOption(nnLoadFromCCDB, int, 0, "", 0, "If 1 networks are fetched from ccdb, else locally") +AddOption(nnCCDBDumpToFile, int, 0, "", 0, "If 1, additionally dump fetched CCDB networks to nnLocalFolder") AddOption(nnLocalFolder, std::string, ".", "", 0, "Local folder in which the networks will be fetched") -AddOption(nnCCDBURL, std::string, "http://ccdb-test.cern.ch:8080", "", 0, "The CCDB URL from where the network files are fetched") AddOption(nnCCDBPath, std::string, "Users/c/csonnabe/TPC/Clusterization", "", 0, "Folder path containing the networks") -AddOption(nnCCDBWithMomentum, int, 1, "", 0, "Distinguishes between the network with and without momentum output for the regression") +AddOption(nnCCDBWithMomentum, std::string, "", "", 0, "Distinguishes between the network with and without momentum output for the regression") AddOption(nnCCDBClassificationLayerType, std::string, "FC", "", 0, "Distinguishes between network with different layer types. Options: FC, CNN") -AddOption(nnCCDBRegressionLayerType, std::string, "CNN", "", 0, "Distinguishes between network with different layer types. Options: FC, CNN") -AddOption(nnCCDBBeamType, std::string, "PbPb", "", 0, "Distinguishes between networks trained for different beam types. Options: PbPb, pp") -AddOption(nnCCDBInteractionRate, int, 50, "", 0, "Distinguishes between networks for different interaction rates [kHz].") +AddOption(nnCCDBRegressionLayerType, std::string, "FC", "", 0, "Distinguishes between network with different layer types. Options: FC, CNN") +AddOption(nnCCDBBeamType, std::string, "pp", "", 0, "Distinguishes between networks trained for different beam types. Options: pp, pPb, PbPb") +AddOption(nnCCDBInteractionRate, std::string, "500", "", 0, "Distinguishes between networks for different interaction rates [kHz].") AddHelp("help", 'h') EndConfig() @@ -293,7 +301,6 @@ BeginSubConfig(GPUSettingsProcessing, proc, configStandalone, "PROC", 0, "Proces AddOption(deviceNum, int32_t, -1, "gpuDevice", 0, "Set GPU device to use (-1: automatic, -2: for round-robin usage in timeslice-pipeline)") AddOption(gpuDeviceOnly, bool, false, "", 0, "Use only GPU as device (i.e. no CPU for OpenCL)") AddOption(globalInitMutex, bool, false, "", 0, "Use global mutex to synchronize initialization of multiple GPU instances") -AddOption(stuckProtection, int32_t, 0, "", 0, "Timeout in us, When AMD GPU is stuck, just continue processing and skip tracking, do not crash or stall the chain") AddOption(trdNCandidates, int32_t, 3, "", 0, "Number of branching track candidates for single input track during propagation") AddOption(trdTrackModelO2, bool, false, "", 0, "Use O2 track model instead of GPU track model for TRD tracking") AddOption(debugLevel, int32_t, -1, "debug", 'd', "Set debug level (-2 = silent, -1 = autoselect (-2 for O2, 0 for standalone))") @@ -302,7 +309,7 @@ AddOption(debugMask, uint32_t, (1 << 18) - 1, "debugMask", 0, "Mask for debug ou AddOption(debugLogSuffix, std::string, "", "debugSuffix", 0, "Suffix for debug log files with --debug 6") AddOption(serializeGPU, int8_t, 0, "", 0, "Synchronize after each kernel call (bit 1) and DMA transfer (bit 2) and identify failures") AddOption(recoTaskTiming, bool, 0, "", 0, "Perform summary timing after whole reconstruction tasks") -AddOption(deterministicGPUReconstruction, int32_t, -1, "", 0, "Make CPU and GPU debug output comparable (sort / skip concurrent parts), -1 = automatic if debugLevel >= 6", def(1)) +AddOption(deterministicGPUReconstruction, int32_t, -1, "", 0, "Make CPU and GPU debug output comparable (sort / skip concurrent parts), -1 = automatic if debugLevel >= 6 or deterministic compile flag set", def(1)) AddOption(showOutputStat, bool, false, "", 0, "Print some track output statistics") AddOption(runCompressionStatistics, bool, false, "compressionStat", 0, "Run statistics and verification for cluster compression") AddOption(resetTimers, int8_t, 1, "", 0, "Reset timers every event") @@ -375,7 +382,7 @@ AddOption(debugOnFailureMaxN, uint32_t, 1, "", 0, "Max number of times to run th AddOption(debugOnFailureMaxFiles, uint32_t, 0, "", 0, "Max number of files to have in the target folder") AddOption(debugOnFailureMaxSize, uint32_t, 0, "", 0, "Max size of existing dumps in the target folder in GB") AddOption(debugOnFailureDirectory, std::string, ".", "", 0, "Target folder for debug / dump") -AddOption(amdMI100SerializationWorkaround, bool, false, "", 0, "Enable workaround that mitigates MI100 serialization bug") +AddOption(memoryStat, bool, false, "", 0, "Print memory statistics") AddVariable(eventDisplay, o2::gpu::GPUDisplayFrontendInterface*, nullptr) AddSubConfig(GPUSettingsProcessingRTC, rtc) AddSubConfig(GPUSettingsProcessingRTCtechnical, rtctech) @@ -507,9 +514,10 @@ AddOption(filterPID, int32_t, -1, "", 0, "Filter for Particle Type (0 Electron, AddOption(nativeFitResolutions, bool, false, "", 0, "Create resolution histograms in the native fit units (sin(phi), tan(lambda), Q/Pt)") AddOption(enableLocalOutput, bool, true, "", 0, "Enable normal output to local PDF files / console") AddOption(dumpToROOT, int32_t, 0, "", 0, "Dump all clusters and tracks to a ROOT file, 1 = combined TNTUple dump, 2 = also individual cluster / track branch dump") +AddOption(writeFileExt, std::string, "", "", 0, "Write extra output file with given extension (default ROOT Canvas)", def("root")) AddOption(writeMCLabels, bool, false, "", 0, "Store mc labels to file for later matching") -AddOption(writeRootFiles, bool, false, "", 0, "Create ROOT canvas files") AddOptionVec(matchMCLabels, std::string, "", 0, "Read labels from files and match them, only process tracks where labels differ") +AddOption(compareTrackStatus, uint32_t, 0, "", 0, "0 = disabled, 1 = write status file, 2 = read status file and compare with current tracks") AddOption(matchDisplayMinPt, float, 0, "", 0, "Minimum Pt of a matched track to be displayed") AddOption(noMC, bool, false, "", 0, "Force running QA without MC labels even if present") AddOption(shipToQC, bool, false, "", 0, "Do not write output files but ship histograms for QC") @@ -519,7 +527,10 @@ AddOption(histMaxNClusters, uint32_t, 500000000, "", 0, "Maximum number of clust AddOption(minNClFindable, uint32_t, 70, "", 0, "Minimum number of (weighted) MC clusters for a track to count as findable") AddOption(minNClEff, uint32_t, 10, "", 0, "Minimum number of (weighted) MC clusters for a track to contribute to all-tracks efficiency histogramm") AddOption(minNClRes, uint32_t, 40, "", 0, "Minimum number of (weighted) MC clusters for a track to contribute to resolution histogram") -AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "--qa", "--QAinputHistogramsOnly") +AddOption(perfFigure, std::string, "", "", 0, "Show as performance figure, provide mc/MC or data as asgument, or a custom string", def("MC")) +AddOption(plotsDir, std::string, "plots", "", 0, "Directory to write plots to") +AddOption(plotsNoTitle, bool, false, "", 0, "Do not print titles on figures") +AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "-c", "--qa", "--QAinputHistogramsOnly") AddHelp("help", 'h') EndConfig() @@ -571,6 +582,7 @@ AddOption(noEvents, bool, false, "", 0, "Run without data (e.g. for field visual AddOption(eventDisplay, int32_t, 0, "display", 'd', "Show standalone event display", def(1)) AddOption(eventGenerator, bool, false, "", 0, "Run event generator") AddOption(cont, bool, false, "", 0, "Process continuous timeframe data, even if input is triggered") +AddOption(setMaxTimeBin, int32_t, -2, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for automatic continuous mode, -2 for automatic continuous / triggered") AddOption(outputcontrolmem, uint64_t, 0, "outputMemory", 0, "Use predefined output buffer of this size", min(0ul), message("Using %s bytes as output memory")) AddOption(inputcontrolmem, uint64_t, 0, "inputMemory", 0, "Use predefined input buffer of this size", min(0ul), message("Using %s bytes as input memory")) AddOption(cpuAffinity, int32_t, -1, "", 0, "Pin CPU affinity to this CPU core", min(-1)) @@ -586,9 +598,9 @@ AddOption(zsVersion, int32_t, 2, "", 0, "ZS Version: 1 = 10-bit ADC row based, 2 AddOption(dumpEvents, bool, false, "", 0, "Dump events (after transformation such as encodeZS") AddOption(stripDumpedEvents, bool, false, "", 0, "Remove redundant inputs (e.g. digits and ZS) before dumping") AddOption(printSettings, int32_t, 0, "", 0, "Print all settings", def(1)) -AddOption(memoryStat, bool, false, "", 0, "Print memory statistics") AddOption(testSyncAsync, bool, false, "syncAsync", 0, "Test first synchronous and then asynchronous processing") AddOption(testSync, bool, false, "sync", 0, "Test settings for synchronous phase") +AddOption(testSyncAsyncQcInSync, bool, false, "syncAsyncSyncQC", 0, "Run QC in sync phase of testSyncAsync") AddOption(timeFrameTime, bool, false, "tfTime", 0, "Print some debug information about time frame processing time") AddOption(controlProfiler, bool, false, "", 0, "Issues GPU profiler stop and start commands to profile only the relevant processing part") AddOption(preloadEvents, bool, false, "", 0, "Preload events into host memory before start processing") @@ -612,7 +624,7 @@ AddSubConfig(GPUSettingsEG, EG) EndConfig() #endif // BeginConfig -//Settings for the O2 workfllow +//Settings for the O2 workflow #if !defined(QCONFIG_PARSER_CXX) && (defined(GPUCA_O2_LIB) || defined(GPUCA_O2_INTERFACE)) BeginSubConfig(GPUSettingsO2, global, configStandalone, "O2", 0, "O2 workflow settings", global) AddOption(solenoidBzNominalGPU, float, -1e6f, "", 0, "Field strength of solenoid Bz in kGaus") @@ -624,6 +636,9 @@ AddOption(deviceType, std::string, "CPU", "", 0, "Device type, CPU | CUDA | HIP AddOption(forceDeviceType, bool, true, "", 0, "force device type, otherwise allows fall-back to CPU") AddOption(synchronousProcessing, bool, false, "", 0, "Apply performance shortcuts for synchronous processing, disable unneeded steps") AddOption(dump, int32_t, 0, "", 0, "Dump events for standalone benchmark: 1 = dump events, 2 = dump events and skip processing in workflow") +AddOption(dumpFirst, int32_t, 0, "", 0, "First event to dump (referring to tfCounter)") +AddOption(dumpLast, int32_t, -1, "", 0, "Last event to dump (-1 = all)") +AddOption(dumpFolder, std::string, "", "", 0, "Folder to which to write dump files, [P] is replaced by process id") AddOption(display, bool, false, "", 0, "Enable standalone gpu tracking visualizaion") AddOption(rundEdx, int32_t, -1, "", 0, "Enable/disable dEdx processing (-1 for autoselect)") AddOption(dEdxSplineTopologyCorrFile, std::string, "", "", 0, "File name of the dE/dx spline track topology correction file") diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv new file mode 100644 index 0000000000000..fc27de72ea2f1 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv @@ -0,0 +1,113 @@ +Architecture,default,default_cpu,MI100,VEGA,TAHITI,TESLA,FERMI,PASCAL,KEPLER,AMPERE,TURING +,,,,,,,,,,, +CORE:,,,,,,,,,,, +WARP_SIZE,32,,64,64,32,32,32,32,32,32,32 +THREAD_COUNT_DEFAULT,256,,256,256,,,,,,512,512 +,,,,,,,,,,, +LB:,,,,,,,,,,, +GPUTPCCreateTrackingData,256,,"[256, 7]","[192, 2]",,,,,,384,256 +GPUTPCTrackletConstructor,256,,"[768, 8]","[512, 10]","[256, 2]","[256, 1]","[256, 2]","[1024, 2]","[512, 4]","[256, 2]","[256, 2]" +GPUTPCTrackletSelector,256,,"[384, 5]","[192, 10]","[256, 3]","[256, 1]","[256, 3]","[512, 4]","[256, 3]","[192, 3]","[192, 3]" +GPUTPCNeighboursFinder,256,,"[192, 8]","[960, 8]",256,256,256,512,256,"[640, 1]","[640, 1]" +GPUTPCNeighboursCleaner,256,,"[128, 5]","[384, 9]",256,256,256,256,256,512,512 +GPUTPCExtrapolationTracking,256,,"[256, 7]","[256, 2]",,,,,,"[128, 4]","[192, 2]" +GPUTRDTrackerKernels_gpuVersion,512,,,,,,,,,, +GPUTPCCreateOccupancyMap_fill,256,,,,,,,,,, +GPUTPCCreateOccupancyMap_fold,256,,,,,,,,,, +GPUTRDTrackerKernels_o2Version,512,,,,,,,,,, +GPUTPCCompressionKernels_step0attached,256,,"[128, 1]","[64, 2]",,,,,,"[64, 2]",128 +GPUTPCCompressionKernels_step1unattached,256,,"[512, 2]","[512, 2]",,,,,,"[512, 3]","[512, 2]" +GPUTPCDecompressionKernels_step0attached,256,,"[128, 2]","[128, 2]",,,,,,"[32, 1]","[32, 1]" +GPUTPCDecompressionKernels_step1unattached,256,,"[64, 2]","[64, 2]",,,,,,"[32, 1]","[32, 1]" +GPUTPCDecompressionUtilKernels_sortPerSectorRow,256,,,,,,,,,, +GPUTPCDecompressionUtilKernels_countFilteredClusters,256,,,,,,,,,, +GPUTPCDecompressionUtilKernels_storeFilteredClusters,256,,,,,,,,,, +GPUTPCCFDecodeZS,"[128, 4]",,"[64, 4]","[64, 1]",,,,,,"[64, 10]","[64, 8]" +GPUTPCCFDecodeZSLink,"""GPUCA_WARP_SIZE""",,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""" +GPUTPCCFDecodeZSDenseLink,"""GPUCA_WARP_SIZE""",,"[""GPUCA_WARP_SIZE"", 4]","[""GPUCA_WARP_SIZE"", 14]",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""" +GPUTPCCFGather,"[1024, 1]",,"[1024, 5]","[1024, 1]",,,,,,"[1024, 1]","[1024, 1]" +COMPRESSION_GATHER,1024,,1024,1024,,,,,,1024,1024 +GPUTPCGMMergerTrackFit,256,,"[192, 2]","[64, 7]",,,,,,"[64, 4]","[32, 8]" +GPUTPCGMMergerFollowLoopers,256,,"[256, 5]","[256, 4]",,,,,,"[64, 12]","[128, 4]" +GPUTPCGMMergerSectorRefit,256,,"[64, 4]","[256, 2]",,,,,,"[32, 6]","[64, 5]" +GPUTPCGMMergerUnpackResetIds,256,,256,256,,,,,,256,256 +GPUTPCGMMergerUnpackGlobal,256,,256,256,,,,,,256,256 +GPUTPCGMMergerResolve_step0,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step1,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step2,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step3,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step4,256,,512,256,,,,,,"[256, 4]","[256, 4]" +GPUTPCGMMergerClearLinks,256,,256,256,,,,,,256,256 +GPUTPCGMMergerMergeWithinPrepare,256,,256,256,,,,,,256,256 +GPUTPCGMMergerMergeSectorsPrepare,256,,256,256,,,,,,"[256, 2]","[256, 2]" +GPUTPCGMMergerMergeBorders_step0,256,,512,256,,,,,,192,192 +GPUTPCGMMergerMergeBorders_step2,256,,512,256,,,,,,"[64, 2]",256 +GPUTPCGMMergerMergeCE,256,,512,256,,,,,,256,256 +GPUTPCGMMergerLinkExtrapolatedTracks,256,,256,256,,,,,,256,256 +GPUTPCGMMergerCollect,256,,"[768, 1]","[1024, 1]",,,,,,"[256, 2]","[128, 2]" +GPUTPCGMMergerSortTracksPrepare,256,,256,256,,,,,,256,256 +GPUTPCGMMergerPrepareForFit_step0,256,,256,256,,,,,,256,256 +GPUTPCGMMergerPrepareForFit_step1,256,,256,256,,,,,,256,256 +GPUTPCGMMergerPrepareForFit_step2,256,,256,256,,,,,,256,256 +GPUTPCGMMergerFinalize_step0,256,,,256,,,,,,, +GPUTPCGMMergerFinalize_step1,256,,,256,,,,,,, +GPUTPCGMMergerFinalize_step2,256,,,256,,,,,,, +GPUTPCGMMergerMergeLoopers_step0,256,,,,,,,,,, +GPUTPCGMMergerMergeLoopers_step1,256,,,,,,,,,, +GPUTPCGMMergerMergeLoopers_step2,256,,,,,,,,,, +GPUTPCGMO2Output_prepare,256,,,,,,,,,, +GPUTPCGMO2Output_output,256,,,,,,,,,, +GPUTPCStartHitsFinder,256,,"[1024, 2]","[1024, 7]",256,256,256,256,256,512,512 +GPUTPCStartHitsSorter,256,,"[1024, 5]","[512, 7]",256,256,256,256,256,"[512, 1]","[512, 1]" +GPUTPCCFCheckPadBaseline,576,,"[576, 2]","[576, 2]",,,,,,"[576, 2]", +GPUTPCCFChargeMapFiller_fillIndexMap,512,,512,512,,,,,,448, +GPUTPCCFChargeMapFiller_fillFromDigits,512,,512,512,,,,,,448, +GPUTPCCFChargeMapFiller_findFragmentStart,512,,512,512,,,,,,448, +GPUTPCCFPeakFinder,512,,"[512, 9]","[512, 4]",,,,,,128, +GPUTPCCFNoiseSuppression,512,,512,512,,,,,,448, +GPUTPCCFDeconvolution,512,,"[512, 5]","[512, 5]",,,,,,384, +GPUTPCCFClusterizer,512,,"[448, 3]","[512, 2]",,,,,,448, +GPUTPCNNClusterizerKernels,512,,,,,,,,,, +GPUTrackingRefitKernel_mode0asGPU,256,,,,,,,,,, +GPUTrackingRefitKernel_mode1asTrackParCov,256,,,,,,,,,, +GPUMemClean16,"[""GPUCA_THREAD_COUNT_DEFAULT"", 1]",,,,,,,,,, +GPUitoa,"[""GPUCA_THREAD_COUNT_DEFAULT"", 1]",,,,,,,,,, +GPUTPCCFNoiseSuppression_noiseSuppression,"""GPUCA_LB_GPUTPCCFNoiseSuppression""",,,,,,,,,, +GPUTPCCFNoiseSuppression_updatePeaks,"""GPUCA_LB_GPUTPCCFNoiseSuppression""",,,,,,,,,, +GPUTPCNNClusterizerKernels_runCfClusterizer,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_fillInputNNCPU,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_fillInputNNGPU,1024,,,,,,,,,, +GPUTPCNNClusterizerKernels_determineClass1Labels,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_determineClass2Labels,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_publishClass1Regression,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_publishClass2Regression,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_publishDeconvolutionFlags,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanStart,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanUp,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanTop,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanDown,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_compactDigits,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCompressionGatherKernels_unbuffered,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_buffered32,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_buffered64,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_buffered128,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_multiBlock,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCGMMergerFinalize_0,256,,256,,,,,,,256,256 +GPUTPCGMMergerFinalize_1,256,,256,,,,,,,256,256 +GPUTPCGMMergerFinalize_2,256,,256,,,,,,,256,256 +,,,,,,,,,,, +PAR:,,,,,,,,,,, +AMD_EUS_PER_CU,0,0,4,4,,,,,,, +SORT_STARTHITS,1,0,,,,,,,,, +NEIGHBOURS_FINDER_MAX_NNEIGHUP,6,0,10,4,,,,,,4,4 +NEIGHBOURS_FINDER_UNROLL_GLOBAL,4,0,4,2,,,,,,, +NEIGHBOURS_FINDER_UNROLL_SHARED,1,0,0,0,,,,,,, +TRACKLET_SELECTOR_HITS_REG_SIZE,12,0,9,27,,,,,,20,20 +ALTERNATE_BORDER_SORT,0,0,1,1,,,,,,1,1 +SORT_BEFORE_FIT,0,0,1,1,,,,,,1,1 +NO_ATOMIC_PRECHECK,0,0,1,1,,,,,,1,1 +DEDX_STORAGE_TYPE,"""float""","""float""","""uint16_t""","""uint16_t""",,,,,,"""uint16_t""","""uint16_t""" +MERGER_INTERPOLATION_ERROR_TYPE,"""float""","""float""","""half""","""half""",,,,,,"""half""","""half""" +COMP_GATHER_KERNEL,0,0,4,4,,,,,,4,4 +COMP_GATHER_MODE,2,0,3,3,,,,,,3,3 +CF_SCAN_WORKGROUP_SIZE,512,0,,,,,,,,, diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example new file mode 100644 index 0000000000000..3c6f1af1aab2f --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example @@ -0,0 +1,617 @@ +{ + "CORE": { + "WARP_SIZE": { + "default": 32, + "MI100": 64, + "VEGA": 64, + "TAHITI": 32, + "TESLA": 32, + "FERMI": 32, + "PASCAL": 32, + "KEPLER": 32, + "AMPERE": 32, + "TURING": 32 + }, + "THREAD_COUNT_DEFAULT": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 512, + "TURING": 512 + } + }, + "LB": { + "GPUTPCCreateTrackingData": { + "default": 256, + "MI100": [256, 7], + "VEGA": [192, 2], + "AMPERE": 384, + "TURING": 256 + }, + "GPUTPCTrackletConstructor": { + "default": 256, + "MI100": [768, 8], + "VEGA": [512, 10], + "TAHITI": [256, 2], + "TESLA": [256, 1], + "FERMI": [256, 2], + "PASCAL": [1024, 2], + "KEPLER": [512, 4], + "AMPERE": [256, 2], + "TURING": [256, 2] + }, + "GPUTPCTrackletSelector": { + "default": 256, + "MI100": [384, 5], + "VEGA": [192, 10], + "TAHITI": [256, 3], + "TESLA": [256, 1], + "FERMI": [256, 3], + "PASCAL": [512, 4], + "KEPLER": [256, 3], + "AMPERE": [192, 3], + "TURING": [192, 3] + }, + "GPUTPCNeighboursFinder": { + "default": 256, + "MI100": [192, 8], + "VEGA": [960, 8], + "TAHITI": 256, + "TESLA": 256, + "FERMI": 256, + "PASCAL": 512, + "KEPLER": 256, + "AMPERE": [640, 1], + "TURING": [640, 1] + }, + "GPUTPCNeighboursCleaner": { + "default": 256, + "MI100": [128, 5], + "VEGA": [384, 9], + "TAHITI": 256, + "TESLA": 256, + "FERMI": 256, + "PASCAL": 256, + "KEPLER": 256, + "AMPERE": 512, + "TURING": 512 + }, + "GPUTPCExtrapolationTracking": { + "default": 256, + "MI100": [256, 7], + "VEGA": [256, 2], + "AMPERE": [128, 4], + "TURING": [192, 2] + }, + "GPUTRDTrackerKernels_gpuVersion": { + "default": 512 + }, + "GPUTPCCreateOccupancyMap_fill": { + "default": 256 + }, + "GPUTPCCreateOccupancyMap_fold": { + "default": 256 + }, + "GPUTRDTrackerKernels_o2Version": { + "default": 512 + }, + "GPUTPCCompressionKernels_step0attached": { + "default": 256, + "MI100": [128, 1], + "VEGA": [64, 2], + "AMPERE": [64, 2], + "TURING": 128 + }, + "GPUTPCCompressionKernels_step1unattached": { + "default": 256, + "MI100": [512, 2], + "VEGA": [512, 2], + "AMPERE": [512, 3], + "TURING": [512, 2] + }, + "GPUTPCDecompressionKernels_step0attached": { + "default": 256, + "MI100": [128, 2], + "VEGA": [128, 2], + "AMPERE": [32, 1], + "TURING": [32, 1] + }, + "GPUTPCDecompressionKernels_step1unattached": { + "default": 256, + "MI100": [64, 2], + "VEGA": [64, 2], + "AMPERE": [32, 1], + "TURING": [32, 1] + }, + "GPUTPCDecompressionUtilKernels_sortPerSectorRow": { + "default": 256 + }, + "GPUTPCDecompressionUtilKernels_countFilteredClusters": { + "default": 256 + }, + "GPUTPCDecompressionUtilKernels_storeFilteredClusters": { + "default": 256 + }, + "GPUTPCCFDecodeZS": { + "default": [128, 4], + "MI100": [64, 4], + "VEGA": [64, 1], + "AMPERE": [64, 10], + "TURING": [64, 8] + }, + "GPUTPCCFDecodeZSLink": { + "default": "GPUCA_WARP_SIZE", + "MI100": "GPUCA_WARP_SIZE", + "VEGA": "GPUCA_WARP_SIZE", + "AMPERE": "GPUCA_WARP_SIZE", + "TURING": "GPUCA_WARP_SIZE" + }, + "GPUTPCCFDecodeZSDenseLink": { + "default": "GPUCA_WARP_SIZE", + "MI100": ["GPUCA_WARP_SIZE", 4], + "VEGA": ["GPUCA_WARP_SIZE", 14], + "AMPERE": "GPUCA_WARP_SIZE", + "TURING": "GPUCA_WARP_SIZE" + }, + "GPUTPCCFGather": { + "default": [1024, 1], + "MI100": [1024, 5], + "VEGA": [1024, 1], + "AMPERE": [1024, 1], + "TURING": [1024, 1] + }, + "COMPRESSION_GATHER": { + "default": 1024, + "MI100": 1024, + "VEGA": 1024, + "AMPERE": 1024, + "TURING": 1024 + }, + "GPUTPCGMMergerTrackFit": { + "default": 256, + "MI100": [192, 2], + "VEGA": [64, 7], + "AMPERE": [64, 4], + "TURING": [32, 8] + }, + "GPUTPCGMMergerFollowLoopers": { + "default": 256, + "MI100": [256, 5], + "VEGA": [256, 4], + "AMPERE": [64, 12], + "TURING": [128, 4] + }, + "GPUTPCGMMergerSectorRefit": { + "default": 256, + "MI100": [64, 4], + "VEGA": [256, 2], + "AMPERE": [32, 6], + "TURING": [64, 5] + }, + "GPUTPCGMMergerUnpackResetIds": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerUnpackGlobal": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step0": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step1": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step2": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step3": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step4": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": [256, 4], + "TURING": [256, 4] + }, + "GPUTPCGMMergerClearLinks": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerMergeWithinPrepare": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerMergeSectorsPrepare": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": [256, 2], + "TURING": [256, 2] + }, + "GPUTPCGMMergerMergeBorders_step0": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 192, + "TURING": 192 + }, + "GPUTPCGMMergerMergeBorders_step2": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": [64, 2], + "TURING": 256 + }, + "GPUTPCGMMergerMergeCE": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerLinkExtrapolatedTracks": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerCollect": { + "default": 256, + "MI100": [768, 1], + "VEGA": [1024, 1], + "AMPERE": [256, 2], + "TURING": [128, 2] + }, + "GPUTPCGMMergerSortTracksPrepare": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerPrepareForFit_step0": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerPrepareForFit_step1": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerPrepareForFit_step2": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerFinalize_step0": { + "default": 256, + "VEGA": 256 + }, + "GPUTPCGMMergerFinalize_step1": { + "default": 256, + "VEGA": 256 + }, + "GPUTPCGMMergerFinalize_step2": { + "default": 256, + "VEGA": 256 + }, + "GPUTPCGMMergerMergeLoopers_step0": { + "default": 256 + }, + "GPUTPCGMMergerMergeLoopers_step1": { + "default": 256 + }, + "GPUTPCGMMergerMergeLoopers_step2": { + "default": 256 + }, + "GPUTPCGMO2Output_prepare": { + "default": 256 + }, + "GPUTPCGMO2Output_output": { + "default": 256 + }, + "GPUTPCStartHitsFinder": { + "default": 256, + "MI100": [1024, 2], + "VEGA": [1024, 7], + "TAHITI": 256, + "TESLA": 256, + "PASCAL": 256, + "FERMI": 256, + "KEPLER": 256, + "AMPERE": 512, + "TURING": 512 + }, + "GPUTPCStartHitsSorter": { + "default": 256, + "MI100": [1024, 5], + "VEGA": [512, 7], + "TAHITI": 256, + "TESLA": 256, + "PASCAL": 256, + "FERMI": 256, + "KEPLER": 256, + "AMPERE": [512, 1], + "TURING": [512, 1] + }, + "GPUTPCCFCheckPadBaseline": { + "default": 576, + "MI100": [576, 2], + "VEGA": [576, 2], + "AMPERE": [576, 2] + }, + "GPUTPCCFChargeMapFiller_fillIndexMap": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFChargeMapFiller_fillFromDigits": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFChargeMapFiller_findFragmentStart": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFPeakFinder": { + "default": 512, + "MI100": [512, 9], + "VEGA": [512, 4], + "AMPERE": 128 + }, + "GPUTPCCFNoiseSuppression": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFDeconvolution": { + "default": 512, + "MI100": [512, 5], + "VEGA": [512, 5], + "AMPERE": 384 + }, + "GPUTPCCFClusterizer": { + "default": 512, + "MI100": [448, 3], + "VEGA": [512, 2], + "AMPERE": 448 + }, + "GPUTPCNNClusterizerKernels": { + "default": 512 + }, + "GPUTrackingRefitKernel_mode0asGPU": { + "default": 256 + }, + "GPUTrackingRefitKernel_mode1asTrackParCov": { + "default": 256 + }, + "GPUMemClean16": { + "default": ["GPUCA_THREAD_COUNT_DEFAULT", 1] + }, + "GPUitoa": { + "default": ["GPUCA_THREAD_COUNT_DEFAULT", 1] + }, + "GPUTPCCFNoiseSuppression_noiseSuppression": { + "default": "GPUCA_LB_GPUTPCCFNoiseSuppression" + }, + "GPUTPCCFNoiseSuppression_updatePeaks": { + "default": "GPUCA_LB_GPUTPCCFNoiseSuppression" + }, + "GPUTPCNNClusterizerKernels_runCfClusterizer": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_fillInputNNCPU": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_fillInputNNGPU": { + "default": 1024 + }, + "GPUTPCNNClusterizerKernels_determineClass1Labels": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_determineClass2Labels": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_publishClass1Regression": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_publishClass2Regression": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_publishDeconvolutionFlags": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCCFStreamCompaction_scanStart": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_scanUp": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_scanTop": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_scanDown": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_compactDigits": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCompressionGatherKernels_unbuffered": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_buffered32": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_buffered64": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_buffered128": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_multiBlock": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCGMMergerFinalize_0": { + "default": 256, + "MI100": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerFinalize_1": { + "default": 256, + "MI100": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerFinalize_2": { + "default": 256, + "MI100": 256, + "AMPERE": 256, + "TURING": 256 + } + }, + "PAR": { + "AMD_EUS_PER_CU": { + "default": 0, + "default_cpu": 0, + "MI100": 4, + "VEGA": 4 + }, + "SORT_STARTHITS": { + "default": 1, + "default_cpu": 0 + }, + "NEIGHBOURS_FINDER_MAX_NNEIGHUP": { + "default": 6, + "default_cpu": 0, + "MI100": 10, + "VEGA": 4, + "AMPERE": 4, + "TURING": 4 + }, + "NEIGHBOURS_FINDER_UNROLL_GLOBAL": { + "default": 4, + "default_cpu": 0, + "MI100": 4, + "VEGA": 2 + }, + "NEIGHBOURS_FINDER_UNROLL_SHARED": { + "default": 1, + "default_cpu": 0, + "MI100": 0, + "VEGA": 0 + }, + "TRACKLET_SELECTOR_HITS_REG_SIZE": { + "default": 12, + "default_cpu": 0, + "MI100": 9, + "VEGA": 27, + "AMPERE": 20, + "TURING": 20 + }, + "ALTERNATE_BORDER_SORT": { + "default": 0, + "default_cpu": 0, + "MI100": 1, + "VEGA": 1, + "AMPERE": 1, + "TURING": 1 + }, + "SORT_BEFORE_FIT": { + "default": 0, + "default_cpu": 0, + "MI100": 1, + "VEGA": 1, + "AMPERE": 1, + "TURING": 1 + }, + "NO_ATOMIC_PRECHECK": { + "default": 0, + "default_cpu": 0, + "MI100": 1, + "VEGA": 1, + "AMPERE": 1, + "TURING": 1 + }, + "DEDX_STORAGE_TYPE": { + "default": "float", + "default_cpu": "float", + "MI100": "uint16_t", + "VEGA": "uint16_t", + "AMPERE": "uint16_t", + "TURING": "uint16_t" + }, + "MERGER_INTERPOLATION_ERROR_TYPE": { + "default": "float", + "default_cpu": "float", + "MI100": "half", + "VEGA": "half", + "AMPERE": "half", + "TURING": "half" + }, + "COMP_GATHER_KERNEL": { + "default": 0, + "default_cpu": 0, + "MI100": 4, + "VEGA": 4, + "AMPERE": 4, + "TURING": 4 + }, + "COMP_GATHER_MODE": { + "default": 2, + "default_cpu": 0, + "MI100": 3, + "VEGA": 3, + "AMPERE": 3, + "TURING": 3 + }, + "CF_SCAN_WORKGROUP_SIZE": { + "default": 512, + "default_cpu": 0 + } + } +} diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh new file mode 100755 index 0000000000000..373bd18ba7cd4 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +[[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } + +DELIM=$'\xFF' +sed -E \ + ':loop + s/^(([^"]*"[^"]*")*[^"]*),/\1'$DELIM'/; + t loop' \ + $1 | \ +awk -F$DELIM \ + 'BEGIN { + print "{" + } { + if (count == 0) { + for (i = 1; i <= NF; i++) { + names[i] = $i + } + } else if ($1 == "CORE:" || $1 == "LB:" || $1 == "PAR:") { + if (paramprinted) print "\n }" + else if (lineprinted) print "" + if (catprinted) print " }," + lineprinted = 0 + paramprinted = 0 + catprinted = 1 + gsub(/:$/, "", $1) + print " \""$1"\": {"; + } else if ($1 != "") { + if (lineprinted) print "" + if (paramprinted) print " }," + lineprinted = 0 + paramprinted = 1 + print " \""$1"\": {"; + lineprinted = 0 + for (i=2; i<=NF; i++) { + if ($i != "") { + gsub(/^"/, "", $i) + gsub(/"$/, "", $i) + gsub(/""/, "\"", $i) + if (lineprinted) print "," + lineprinted = 1 + printf(" \"%s\": %s", names[i], $i) + } + } + } + count++; + } END { + if (paramprinted) print "\n }" + if (catprinted) print " }" + print "}" + }' diff --git a/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake b/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake new file mode 100644 index 0000000000000..d0e36e7f15026 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake @@ -0,0 +1,135 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# file gpu_param_header_generator.cmake +# author Gabriele Cimador + +function(generate_gpu_param_header GPU_PARAM_JSON_FILES ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) + list(FIND ARCH_LIST "ALL" do_all_architectures) + list(FIND ARCH_LIST "AUTO" do_auto_architectures) + if(do_all_architectures GREATER -1 OR do_auto_architectures GREATER -1) + if(do_auto_architectures GREATER -1) + detect_gpu_arch("AUTO") + list(REMOVE_ITEM ARCH_LIST "AUTO") + else() + detect_gpu_arch("ALL") + endif() + list(APPEND ARCH_LIST ${TARGET_ARCH}) + endif() + + # Types + set(TYPES CORE LB PAR) + set(ARCH_LIST_EXT "${ARCH_LIST};default;default_cpu") + # Per architecture definitions + set(JSON_ARCHITECTURES) + + set(GPU_PARAM_JSON_N_FILES 0) + foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON_FILES) + file(READ "${GPU_PARAM_JSON_FILE}" JSON_CONTENT) + foreach(TYPE IN LISTS TYPES) + string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") + math(EXPR last "${n_params} - 1") + foreach(i RANGE 0 ${last}) + string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") + string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") + if(n_archs GREATER 0) + math(EXPR last_arch "${n_archs} - 1") + foreach(iArch RANGE 0 ${last_arch}) + string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") + if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") + message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") + endif() + if(arch MATCHES ^default AND GPU_PARAM_JSON_N_FILES GREATER 0) + message(FATAL_ERROR "Defaults must be provided in first parameter file") + endif() + if(do_all_architectures GREATER -1) + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") + endif() + set(list_idx 0) + else() + list(FIND ARCH_LIST_EXT "${arch}" list_idx) + endif() + if(list_idx GREATER -1) + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") + if(TYPE STREQUAL "LB") + set(MACRO_NAME "GPUCA_LB_${param_name}") + elseif(TYPE STREQUAL "PAR") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + else() + set(MACRO_NAME "GPUCA_${param_name}") + endif() + set(vals "${param_values}") + string(REGEX REPLACE "^\\[ *" "" vals "${vals}") + string(REGEX REPLACE " *\\]$" "" vals "${vals}") + string(REGEX REPLACE "\"" "" vals "${vals}") + set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") + if(arch MATCHES ^default) + # fallback defaults are wrapped in #ifndef + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + else() + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + endif() + endif() + endforeach() + endif() + endforeach() + endforeach() + math(EXPR GPU_PARAM_JSON_N_FILES "${GPU_PARAM_JSON_N_FILES} + 1") + endforeach() + + list(REMOVE_DUPLICATES JSON_ARCHITECTURES) + list(SORT JSON_ARCHITECTURES) + if(ARGC GREATER 4) + set(${ARGV4} "${JSON_ARCHITECTURES}" PARENT_SCOPE) + endif() + if(do_all_architectures GREATER -1) + list(REMOVE_ITEM ARCH_LIST "ALL") + list(APPEND ARCH_LIST ${JSON_ARCHITECTURES}) + endif() + list(REMOVE_DUPLICATES ARCH_LIST) + list(SORT ARCH_LIST) + + get_filename_component(DEVICE_HEADER_FILE "${OUT_HEADER_DEVICE}" NAME) + + set(TMP_HEADER "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") + set(TMP_HEADER_DEVICE "#ifndef GPUDEFPARAMETERSDEFAULTSDEVICE_H\n#define GPUDEFPARAMETERSDEFAULTSDEVICE_H\n\n") + string(APPEND TMP_HEADER "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + string(APPEND TMP_HEADER_DEVICE "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + string(APPEND TMP_HEADER_DEVICE "// Architectures: ${TARGET_ARCH}\n\n") + string(APPEND TMP_HEADER "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") + string(APPEND TMP_HEADER "#include \"${DEVICE_HEADER_FILE}\"\n") + + string(APPEND TMP_HEADER_DEVICE "#if 0\n") + foreach(ARCH IN LISTS ARCH_LIST) + if(do_all_architectures EQUAL -1 AND do_auto_architectures EQUAL -1 AND NOT generate_gpu_param_header_OUTPUT_TMP_${ARCH}) + message(FATAL_ERROR "No parameters defined for architecture ${ARCH}") + endif() + string(APPEND TMP_HEADER_DEVICE "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") + string(APPEND TMP_HEADER_DEVICE ${generate_gpu_param_header_OUTPUT_TMP_${ARCH}}) + endforeach() + string(APPEND TMP_HEADER_DEVICE "#else\n#error GPU TYPE NOT SET\n#endif\n") + + # Default parameters + string(APPEND TMP_HEADER "\n// Default parameters if not defined for the target architecture\n\n") + string(APPEND TMP_HEADER ${generate_gpu_param_header_OUTPUT_TMP_default}) + string(APPEND TMP_HEADER "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + + # CPU fallback + string(APPEND TMP_HEADER "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") + string(APPEND TMP_HEADER ${generate_gpu_param_header_OUTPUT_TMP_default_cpu}) + string(APPEND TMP_HEADER "\n#endif // GPUCA_GPUCODE_GENRTC\n") + + string(APPEND TMP_HEADER "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") + string(APPEND TMP_HEADER_DEVICE "\n#endif // GPUDEFPARAMETERSDEFAULTSDEVICE_H\n") + file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") + file(GENERATE OUTPUT "${OUT_HEADER_DEVICE}" CONTENT "${TMP_HEADER_DEVICE}") +endfunction() diff --git a/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python new file mode 100755 index 0000000000000..1ae15662021a3 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +import sys, json, csv, string + +if len(sys.argv) != 3: + sys.exit("usage: json2csv.py input.json output.csv") + +try: + data = json.load(open(sys.argv[1])) +except Exception: + sys.exit("invalid json") + +if set(data) != {"CORE", "LB", "PAR"}: + sys.exit("invalid categories") + +arches = [] +seen = set() +for cat in data.values(): + if not isinstance(cat, dict): + sys.exit("data not 2-dimensional") + for param in cat.values(): + if not isinstance(param, dict): + sys.exit("data not 2-dimensional") + for a in param.keys(): + if a not in seen: + seen.add(a) + arches.append(a) + +cols = 1 + len(arches) +empty = [""] * cols +arches = sorted(arches, key=lambda x: 0 if x.startswith("default") else 1) + +with open(sys.argv[2], "w", newline="") as f: + w = csv.writer(f, lineterminator="\n") + w.writerow(["Architecture", *arches]) + w.writerow(empty) + cats = list(data.items()) + for ci, (cname, cat) in enumerate(cats): + w.writerow([f"{cname}:"] + [""] * (cols - 1)) + for pname, param in cat.items(): + row = [pname] + for a in arches: + v = param.get(a, "") + if isinstance(v, list): + row.append(json.dumps(v)) + elif isinstance(v, str) and not v == "": + row.append('"' + v + '"') + else: + row.append(v) + w.writerow(row) + if ci != len(cats) - 1: + w.writerow(empty) diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h index 8e99514a817c5..46ced1e0481f9 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h @@ -27,6 +27,7 @@ #pragma link C++ struct o2::gpu::GPUTPCGMSectorTrack::sectorTrackParam + ; #pragma link C++ class o2::gpu::trackInterface < o2::gpu::GPUTPCGMTrackParam> + ; #pragma link C++ class o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::gpu::GPUTPCGMTrackParam>> + ; +#pragma link C++ class o2::gpu::GPUTRDRecoParam + ; #pragma link C++ class o2::gpu::gputpcgmmergertypes::GPUTPCOuterParam + ; #pragma link C++ class o2::gpu::gputpcgmmergertypes::InterpolationErrorHit + ; diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h index 46fd50464c69b..7bd2c689c5354 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h @@ -43,5 +43,6 @@ #pragma link C++ class o2::tpc::CalibdEdxTrackTopologyPol + ; #pragma link C++ class o2::tpc::CalibdEdxTrackTopologySpline + ; #pragma link C++ struct o2::tpc::CalibdEdxTrackTopologyPolContainer + ; +#pragma link C++ struct o2::tpc::ORTRootSerializer + ; #endif diff --git a/GPU/GPUTracking/Global/GPUChain.cxx b/GPU/GPUTracking/Global/GPUChain.cxx index 300de31a509ba..fe0cf59c6c28e 100644 --- a/GPU/GPUTracking/Global/GPUChain.cxx +++ b/GPU/GPUTracking/Global/GPUChain.cxx @@ -18,33 +18,33 @@ using namespace o2::gpu; constexpr GPUChain::krnlRunRange GPUChain::krnlRunRangeNone; constexpr GPUChain::krnlEvent GPUChain::krnlEventNone; -GPUChain::krnlExec GPUChain::GetGrid(uint32_t totalItems, uint32_t nThreads, int32_t stream, GPUReconstruction::krnlDeviceType d, GPUDataTypes::RecoStep st) +GPUChain::krnlExec GPUChain::GetGrid(uint32_t totalItems, uint32_t nThreads, int32_t stream, GPUReconstruction::krnlDeviceType d, gpudatatypes::RecoStep st) { const uint32_t nBlocks = (totalItems + nThreads - 1) / nThreads; return {nBlocks, nThreads, stream, d, st}; } -GPUChain::krnlExec GPUChain::GetGrid(uint32_t totalItems, int32_t stream, GPUReconstruction::krnlDeviceType d, GPUDataTypes::RecoStep st) +GPUChain::krnlExec GPUChain::GetGrid(uint32_t totalItems, int32_t stream, GPUReconstruction::krnlDeviceType d, gpudatatypes::RecoStep st) { return {(uint32_t)-1, totalItems, stream, d, st}; } -GPUChain::krnlExec GPUChain::GetGridBlk(uint32_t nBlocks, int32_t stream, GPUReconstruction::krnlDeviceType d, GPUDataTypes::RecoStep st) +GPUChain::krnlExec GPUChain::GetGridBlk(uint32_t nBlocks, int32_t stream, GPUReconstruction::krnlDeviceType d, gpudatatypes::RecoStep st) { return {(uint32_t)-2, nBlocks, stream, d, st}; } -GPUChain::krnlExec GPUChain::GetGridBlkStep(uint32_t nBlocks, int32_t stream, GPUDataTypes::RecoStep st) +GPUChain::krnlExec GPUChain::GetGridBlkStep(uint32_t nBlocks, int32_t stream, gpudatatypes::RecoStep st) { return {(uint32_t)-2, nBlocks, stream, GPUReconstruction::krnlDeviceType::Auto, st}; } -GPUChain::krnlExec GPUChain::GetGridAuto(int32_t stream, GPUReconstruction::krnlDeviceType d, GPUDataTypes::RecoStep st) +GPUChain::krnlExec GPUChain::GetGridAuto(int32_t stream, GPUReconstruction::krnlDeviceType d, gpudatatypes::RecoStep st) { return {(uint32_t)-3, 0, stream, d, st}; } -GPUChain::krnlExec GPUChain::GetGridAutoStep(int32_t stream, GPUDataTypes::RecoStep st) +GPUChain::krnlExec GPUChain::GetGridAutoStep(int32_t stream, gpudatatypes::RecoStep st) { return {(uint32_t)-3, 0, stream, GPUReconstruction::krnlDeviceType::Auto, st}; } diff --git a/GPU/GPUTracking/Global/GPUChain.h b/GPU/GPUTracking/Global/GPUChain.h index e3a20ad81a2cb..907ed7ea97c12 100644 --- a/GPU/GPUTracking/Global/GPUChain.h +++ b/GPU/GPUTracking/Global/GPUChain.h @@ -200,15 +200,15 @@ class GPUChain return mRec->getTimer(name, num); } // Get GRID with NBLOCKS minimal such that nThreads * NBLOCS >= totalItems - krnlExec GetGrid(uint32_t totalItems, uint32_t nThreads, int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, GPUDataTypes::RecoStep st = GPUDataTypes::RecoStep::NoRecoStep); + krnlExec GetGrid(uint32_t totalItems, uint32_t nThreads, int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, gpudatatypes::RecoStep st = gpudatatypes::RecoStep::NoRecoStep); // Get GRID with NBLOCKS minimal such that ideal number of threads * NBLOCKS >= totalItems - krnlExec GetGrid(uint32_t totalItems, int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, GPUDataTypes::RecoStep st = GPUDataTypes::RecoStep::NoRecoStep); + krnlExec GetGrid(uint32_t totalItems, int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, gpudatatypes::RecoStep st = gpudatatypes::RecoStep::NoRecoStep); // Get GRID with specified number of blocks, each block with ideal number of threads - krnlExec GetGridBlk(uint32_t nBlocks, int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, GPUDataTypes::RecoStep st = GPUDataTypes::RecoStep::NoRecoStep); - krnlExec GetGridBlkStep(uint32_t nBlocks, int32_t stream, GPUDataTypes::RecoStep st = GPUDataTypes::RecoStep::NoRecoStep); + krnlExec GetGridBlk(uint32_t nBlocks, int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, gpudatatypes::RecoStep st = gpudatatypes::RecoStep::NoRecoStep); + krnlExec GetGridBlkStep(uint32_t nBlocks, int32_t stream, gpudatatypes::RecoStep st = gpudatatypes::RecoStep::NoRecoStep); // Get GRID with ideal number of threads / blocks for GPU - krnlExec GetGridAuto(int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, GPUDataTypes::RecoStep st = GPUDataTypes::RecoStep::NoRecoStep); - krnlExec GetGridAutoStep(int32_t stream, GPUDataTypes::RecoStep st = GPUDataTypes::RecoStep::NoRecoStep); + krnlExec GetGridAuto(int32_t stream, GPUReconstruction::krnlDeviceType d = GPUReconstruction::krnlDeviceType::Auto, gpudatatypes::RecoStep st = gpudatatypes::RecoStep::NoRecoStep); + krnlExec GetGridAutoStep(int32_t stream, gpudatatypes::RecoStep st = gpudatatypes::RecoStep::NoRecoStep); inline uint32_t BlockCount() const { return mRec->mMultiprocessorCount; } inline uint32_t WarpSize() const { return mRec->mWarpSize; } @@ -224,8 +224,6 @@ class GPUChain inline GPUChain* GetNextChainInQueue() { return mRec->GetNextChainInQueue(); } - virtual int32_t DoStuckProtection(int32_t stream, deviceEvent event) { return 0; } - template bool DoDebugAndDump(RecoStep step, uint32_t mask, T& processor, S T::*func, Args&&... args) { @@ -297,6 +295,7 @@ inline int32_t GPUChain::runRecoStep(RecoStep step, S T::*func, Args... args) } int32_t retVal = (reinterpret_cast(this)->*func)(args...); if (timer) { + SynchronizeGPU(); timer->timerTotal.Stop(); timer->timerCPU += (double)(std::clock() - c) / CLOCKS_PER_SEC; } diff --git a/GPU/GPUTracking/Global/GPUChainITS.cxx b/GPU/GPUTracking/Global/GPUChainITS.cxx index a85cdb48c4d1c..598f7a61cac1a 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.cxx +++ b/GPU/GPUTracking/Global/GPUChainITS.cxx @@ -25,26 +25,27 @@ namespace o2::its class GPUFrameworkExternalAllocator final : public o2::its::ExternalAllocator { public: - GPUFrameworkExternalAllocator(GPUMemoryResource::MemoryType type) : mType(type) {} - - void* allocate(size_t size) override + void* allocate(size_t size) final { return mFWReco->AllocateDirectMemory(size, mType); } - void deallocate(char* ptr, size_t size) override {} + void deallocate(char* ptr, size_t size) final {} // this is a simple no-op + void pushTagOnStack(uint64_t tag) final + { + mFWReco->PushNonPersistentMemory(tag); + } + void popTagOffStack(uint64_t tag) final + { + mFWReco->PopNonPersistentMemory(gpudatatypes::RecoStep::ITSTracking, tag); + } void setReconstructionFramework(o2::gpu::GPUReconstruction* fwr) { mFWReco = fwr; } private: o2::gpu::GPUReconstruction* mFWReco; - GPUMemoryResource::MemoryType mType; }; } // namespace o2::its -GPUChainITS::~GPUChainITS() -{ - mITSTrackerTraits.reset(); - mITSVertexerTraits.reset(); -} +GPUChainITS::~GPUChainITS() = default; GPUChainITS::GPUChainITS(GPUReconstruction* rec) : GPUChain(rec) {} @@ -73,12 +74,9 @@ o2::its::TimeFrame<7>* GPUChainITS::GetITSTimeframe() } #if !defined(GPUCA_STANDALONE) if (mITSTimeFrame->isGPU()) { - mFrameworkDeviceAllocator.reset(new o2::its::GPUFrameworkExternalAllocator(GPUMemoryResource::MEMORY_GPU)); - mFrameworkDeviceAllocator->setReconstructionFramework(rec()); - mITSTimeFrame->setExternalDeviceAllocator(mFrameworkDeviceAllocator.get()); - mFrameworkHostAllocator.reset(new o2::its::GPUFrameworkExternalAllocator(GPUMemoryResource::MEMORY_HOST)); - mFrameworkHostAllocator->setReconstructionFramework(rec()); - mITSTimeFrame->setExternalHostAllocator(mFrameworkHostAllocator.get()); + mFrameworkAllocator.reset(new o2::its::GPUFrameworkExternalAllocator()); + mFrameworkAllocator->setReconstructionFramework(rec()); + mITSTimeFrame->setFrameworkAllocator(mFrameworkAllocator.get()); } #endif return mITSTimeFrame.get(); diff --git a/GPU/GPUTracking/Global/GPUChainITS.h b/GPU/GPUTracking/Global/GPUChainITS.h index ab693bcef3f8b..4aa97f3f47784 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.h +++ b/GPU/GPUTracking/Global/GPUChainITS.h @@ -34,7 +34,7 @@ class GPUChainITS final : public GPUChain friend class GPUReconstruction; public: - ~GPUChainITS() override; + ~GPUChainITS() final; int32_t Init() override; int32_t PrepareEvent() override; int32_t Finalize() override; @@ -50,11 +50,10 @@ class GPUChainITS final : public GPUChain protected: GPUChainITS(GPUReconstruction* rec); + std::unique_ptr mFrameworkAllocator; + std::unique_ptr> mITSTimeFrame; std::unique_ptr> mITSTrackerTraits; std::unique_ptr> mITSVertexerTraits; - std::unique_ptr> mITSTimeFrame; - std::unique_ptr mFrameworkDeviceAllocator; - std::unique_ptr mFrameworkHostAllocator; }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 91870f981d542..7216de0535329 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -34,6 +34,7 @@ #include "GPUTRDTracker.h" #include "AliHLTTPCRawCluster.h" #include "GPUTRDTrackletLabels.h" +#include "GPUTRDRecoParam.h" #include "display/GPUDisplayInterface.h" #include "GPUQA.h" #include "GPULogging.h" @@ -168,78 +169,78 @@ void GPUChainTracking::MemorySize(size_t& gpuMem, size_t& pageLockedHostMem) bool GPUChainTracking::ValidateSteps() { - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCdEdx) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging)) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCdEdx) && !(GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging)) { GPUError("Invalid Reconstruction Step Setting: dEdx requires TPC Merger to be active"); return false; } - if ((GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCdEdx) && !(GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging)) { + if ((GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCdEdx) && !(GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCMerging)) { GPUError("Invalid GPU Reconstruction Step Setting: dEdx requires TPC Merger to be active"); return false; } - if (((GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging)) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCConversion)) { + if (((GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging)) && !(GetRecoSteps() & gpudatatypes::RecoStep::TPCConversion)) { GPUError("Invalid Reconstruction Step Setting: Tracking requires TPC Conversion to be active"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCClusterFinding) && !(GetRecoStepsInputs() & GPUDataTypes::InOutType::TPCRaw)) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCClusterFinding) && !(GetRecoStepsInputs() & gpudatatypes::InOutType::TPCRaw)) { GPUError("Invalid input, TPC Clusterizer needs TPC raw input"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCConversion)) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging) && !(GetRecoSteps() & gpudatatypes::RecoStep::TPCConversion)) { GPUError("Invalid input / output / step, merger cannot read/store sectors tracks and needs TPC conversion"); return false; } - bool tpcClustersAvail = (GetRecoStepsInputs() & GPUDataTypes::InOutType::TPCClusters) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCClusterFinding) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCDecompression); - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging) && !tpcClustersAvail) { + bool tpcClustersAvail = (GetRecoStepsInputs() & gpudatatypes::InOutType::TPCClusters) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCClusterFinding) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCDecompression); + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging) && !tpcClustersAvail) { GPUError("Invalid Inputs for track merging, TPC Clusters required"); return false; } #ifndef GPUCA_TPC_GEOMETRY_O2 - if (GetRecoSteps() & GPUDataTypes::RecoStep::TPCClusterFinding) { + if (GetRecoSteps() & gpudatatypes::RecoStep::TPCClusterFinding) { GPUError("Can not run TPC GPU Cluster Finding with Run 2 Data"); return false; } #endif - if (((GetRecoSteps() & GPUDataTypes::RecoStep::TPCConversion) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCCompression) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCdEdx)) && !tpcClustersAvail) { + if (((GetRecoSteps() & gpudatatypes::RecoStep::TPCConversion) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCCompression) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCdEdx)) && !tpcClustersAvail) { GPUError("Missing input for TPC Cluster conversion / sector tracking / compression / dEdx: TPC Clusters required"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking)) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging) && !(GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking)) { GPUError("Input for TPC merger missing"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCCompression) && !((GetRecoStepsInputs() & GPUDataTypes::InOutType::TPCMergedTracks) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging))) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCCompression) && !((GetRecoStepsInputs() & gpudatatypes::InOutType::TPCMergedTracks) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging))) { GPUError("Input for TPC compressor missing"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TRDTracking) && (!((GetRecoStepsInputs() & GPUDataTypes::InOutType::TPCMergedTracks) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging)) || !(GetRecoStepsInputs() & GPUDataTypes::InOutType::TRDTracklets))) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TRDTracking) && (!((GetRecoStepsInputs() & gpudatatypes::InOutType::TPCMergedTracks) || (GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging)) || !(GetRecoStepsInputs() & gpudatatypes::InOutType::TRDTracklets))) { GPUError("Input for TRD Tracker missing"); return false; } - if ((GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCRaw) || (GetRecoStepsOutputs() & GPUDataTypes::InOutType::TRDTracklets)) { + if ((GetRecoStepsOutputs() & gpudatatypes::InOutType::TPCRaw) || (GetRecoStepsOutputs() & gpudatatypes::InOutType::TRDTracklets)) { GPUError("TPC Raw / TPC Clusters / TRD Tracklets cannot be output"); return false; } - if ((GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCMergedTracks) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging)) { + if ((GetRecoStepsOutputs() & gpudatatypes::InOutType::TPCMergedTracks) && !(GetRecoSteps() & gpudatatypes::RecoStep::TPCMerging)) { GPUError("No TPC Merged Track Output available"); return false; } - if ((GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCCompressedClusters) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCCompression)) { + if ((GetRecoStepsOutputs() & gpudatatypes::InOutType::TPCCompressedClusters) && !(GetRecoSteps() & gpudatatypes::RecoStep::TPCCompression)) { GPUError("No TPC Compression Output available"); return false; } - if ((GetRecoStepsOutputs() & GPUDataTypes::InOutType::TRDTracks) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TRDTracking)) { + if ((GetRecoStepsOutputs() & gpudatatypes::InOutType::TRDTracks) && !(GetRecoSteps() & gpudatatypes::RecoStep::TRDTracking)) { GPUError("No TRD Tracker Output available"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCdEdx) && (processors()->calibObjects.dEdxCalibContainer == nullptr)) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCdEdx) && (processors()->calibObjects.dEdxCalibContainer == nullptr)) { GPUError("Cannot run dE/dx without dE/dx calibration container object"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCClusterFinding) && processors()->calibObjects.tpcPadGain == nullptr) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCClusterFinding) && processors()->calibObjects.tpcPadGain == nullptr) { GPUError("Cannot run gain calibration without calibration object"); return false; } - if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCClusterFinding) && processors()->calibObjects.tpcZSLinkMapping == nullptr && mIOPtrs.tpcZS != nullptr) { + if ((GetRecoSteps() & gpudatatypes::RecoStep::TPCClusterFinding) && processors()->calibObjects.tpcZSLinkMapping == nullptr && mIOPtrs.tpcZS != nullptr) { GPUError("Cannot run TPC ZS Decoder without mapping object. (tpczslinkmapping.dump missing?)"); return false; } @@ -248,7 +249,7 @@ bool GPUChainTracking::ValidateSteps() bool GPUChainTracking::ValidateSettings() { - int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; + int32_t gatherMode = mRec->GetProcessingSettings().tpcCompressionGatherMode == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression).par_COMP_GATHER_MODE : mRec->GetProcessingSettings().tpcCompressionGatherMode; if ((param().rec.tpc.nWays & 1) == 0) { GPUError("nWay setting musst be odd number!"); return false; @@ -265,7 +266,7 @@ bool GPUChainTracking::ValidateSettings() GPUError("NStreams of %d insufficient for %d nTPCClustererLanes", mRec->NStreams(), (int32_t)GetProcessingSettings().nTPCClustererLanes); return false; } - if ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression) && GetProcessingSettings().noGPUMemoryRegistration && gatherMode != 3) { + if ((mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression) && GetProcessingSettings().noGPUMemoryRegistration && gatherMode != 3) { GPUError("noGPUMemoryRegistration only possible with gather mode 3 (set to %d / %d)", mRec->GetProcessingSettings().tpcCompressionGatherMode, gatherMode); return false; } @@ -273,18 +274,22 @@ bool GPUChainTracking::ValidateSettings() GPUError("Clusterizer and merger Sanity checks only supported when not running on GPU"); return false; } - if (GetProcessingSettings().tpcWriteClustersAfterRejection && (mRec->IsGPU() || param().rec.tpc.compressionTypeMask || !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCCompression))) { + if (GetProcessingSettings().tpcWriteClustersAfterRejection && (mRec->IsGPU() || param().rec.tpc.compressionTypeMask || !(GetRecoSteps() & gpudatatypes::RecoStep::TPCCompression))) { GPUError("tpcWriteClustersAfterRejection requires compressionTypeMask = 0, no GPU usage, and compression enabled"); return false; } if (GetProcessingSettings().doublePipeline) { - if (!GetRecoStepsOutputs().isOnlySet(GPUDataTypes::InOutType::TPCMergedTracks, GPUDataTypes::InOutType::TPCCompressedClusters, GPUDataTypes::InOutType::TPCClusters)) { + if (GetProcessingSettings().tpcFreeAllocatedMemoryAfterProcessing) { + GPUError("Cannot use double pipeline with tpcFreeAllocatedMemoryAfterProcessing"); + return false; + } + if (!GetRecoStepsOutputs().isOnlySet(gpudatatypes::InOutType::TPCMergedTracks, gpudatatypes::InOutType::TPCCompressedClusters, gpudatatypes::InOutType::TPCClusters)) { GPUError("Invalid outputs for double pipeline mode 0x%x", (uint32_t)GetRecoStepsOutputs()); return false; } - if (((GetRecoStepsOutputs().isSet(GPUDataTypes::InOutType::TPCCompressedClusters) && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::compressedClusters)] == nullptr) || - (GetRecoStepsOutputs().isSet(GPUDataTypes::InOutType::TPCClusters) && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)] == nullptr) || - (GetRecoStepsOutputs().isSet(GPUDataTypes::InOutType::TPCMergedTracks) && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::tpcTracks)] == nullptr) || + if (((GetRecoStepsOutputs().isSet(gpudatatypes::InOutType::TPCCompressedClusters) && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::compressedClusters)] == nullptr) || + (GetRecoStepsOutputs().isSet(gpudatatypes::InOutType::TPCClusters) && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)] == nullptr) || + (GetRecoStepsOutputs().isSet(gpudatatypes::InOutType::TPCMergedTracks) && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::tpcTracks)] == nullptr) || (GetProcessingSettings().outputSharedClusterMap && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::sharedClusterMap)] == nullptr))) { GPUError("Must use external output for double pipeline mode"); return false; @@ -293,16 +298,16 @@ bool GPUChainTracking::ValidateSettings() GPUError("Double pipeline incompatible to compression mode 1"); return false; } - if (!(GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression) || !(GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding) || param().rec.fwdTPCDigitsAsClusters) { + if (!(GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression) || !(GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding) || param().rec.fwdTPCDigitsAsClusters) { GPUError("Invalid reconstruction settings for double pipeline: Needs compression and cluster finding"); return false; } } - if ((GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression) && !(GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression) && (gatherMode == 1 || gatherMode == 3)) { + if ((GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression) && !(GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression) && (gatherMode == 1 || gatherMode == 3)) { GPUError("Invalid tpcCompressionGatherMode for compression on CPU"); return false; } - if (GetProcessingSettings().tpcApplyClusterFilterOnCPU > 0 && (GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding || GetProcessingSettings().runMC)) { + if (GetProcessingSettings().tpcApplyClusterFilterOnCPU > 0 && (GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding || GetProcessingSettings().runMC)) { GPUError("tpcApplyClusterFilterOnCPU cannot be used with GPU clusterization or with MC labels"); return false; } @@ -328,9 +333,9 @@ int32_t GPUChainTracking::Init() const auto& threadContext = GetThreadContext(); if (GetProcessingSettings().debugLevel >= 1) { printf("Enabled Reconstruction Steps: 0x%x (on GPU: 0x%x)", (int32_t)GetRecoSteps().get(), (int32_t)GetRecoStepsGPU().get()); - for (uint32_t i = 0; i < sizeof(GPUDataTypes::RECO_STEP_NAMES) / sizeof(GPUDataTypes::RECO_STEP_NAMES[0]); i++) { + for (uint32_t i = 0; i < sizeof(gpudatatypes::RECO_STEP_NAMES) / sizeof(gpudatatypes::RECO_STEP_NAMES[0]); i++) { if (GetRecoSteps().isSet(1u << i)) { - printf(" - %s", GPUDataTypes::RECO_STEP_NAMES[i]); + printf(" - %s", gpudatatypes::RECO_STEP_NAMES[i]); if (GetRecoStepsGPU().isSet(1u << i)) { printf(" (G)"); } @@ -431,6 +436,9 @@ void GPUChainTracking::UpdateGPUCalibObjects(int32_t stream, const GPUCalibObjec memcpy((void*)mFlatObjectsShadow.mCalibObjects.trdGeometry, (const void*)processors()->calibObjects.trdGeometry, sizeof(*processors()->calibObjects.trdGeometry)); mFlatObjectsShadow.mCalibObjects.trdGeometry->clearInternalBufferPtr(); } + if (processors()->calibObjects.trdRecoParam && (ptrMask == nullptr || ptrMask->trdRecoParam)) { + memcpy((void*)mFlatObjectsShadow.mCalibObjects.trdRecoParam, (const void*)processors()->calibObjects.trdRecoParam, sizeof(*processors()->calibObjects.trdRecoParam)); + } if (processors()->calibObjects.tpcPadGain && (ptrMask == nullptr || ptrMask->tpcPadGain)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.tpcPadGain, (const void*)processors()->calibObjects.tpcPadGain, sizeof(*processors()->calibObjects.tpcPadGain)); } @@ -471,7 +479,7 @@ int32_t GPUChainTracking::ForceInitQA() qa.reset(new GPUQA(this)); } if (!GetQA()->IsInitialized()) { - return GetQA()->InitQA(); + return GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : gpudatatypes::gpuqa::tasksAutomatic); } return 0; } @@ -532,6 +540,9 @@ void* GPUChainTracking::GPUTrackingFlatObjects::SetPointersFlatObjects(void* mem if (mChainTracking->processors()->calibObjects.trdGeometry) { computePointerWithAlignment(mem, mCalibObjects.trdGeometry, 1); } + if (mChainTracking->processors()->calibObjects.trdRecoParam) { + computePointerWithAlignment(mem, mCalibObjects.trdRecoParam, 1); + } computePointerWithAlignment(mem, mCalibObjects.o2Propagator, 1); if (!mChainTracking->processors()->calibObjects.o2Propagator) { mCalibObjects.o2Propagator = nullptr; // Always reserve memory for o2::Propagator, since it may be propagatred only during run() not during init(). @@ -598,6 +609,12 @@ void GPUChainTracking::SetTRDGeometry(std::unique_ptr&& g processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); } +void GPUChainTracking::SetTRDRecoParam(std::unique_ptr&& par) +{ + mTRDRecoParamU = std::move(par); + processors()->calibObjects.trdRecoParam = mTRDRecoParamU.get(); +} + int32_t GPUChainTracking::DoQueuedUpdates(int32_t stream, bool updateSlave) { int32_t retVal = 0; @@ -636,7 +653,7 @@ int32_t GPUChainTracking::DoQueuedUpdates(int32_t stream, bool updateSlave) pDst[i] = pSrc[i]; } } - if (mNewCalibObjects->trdGeometry && (GetRecoSteps() & GPUDataTypes::RecoStep::TRDTracking)) { + if (mNewCalibObjects->trdGeometry && (GetRecoSteps() & gpudatatypes::RecoStep::TRDTracking)) { if (GetProcessingSettings().trdTrackModelO2) { processors()->trdTrackerO2.UpdateGeometry(); if (mRec->IsGPU()) { @@ -686,7 +703,7 @@ int32_t GPUChainTracking::RunChain() } const bool needQA = GPUQA::QAAvailable() && (GetProcessingSettings().runQA || (GetProcessingSettings().eventDisplay && (mIOPtrs.nMCInfosTPC || GetProcessingSettings().runMC))); if (needQA && GetQA()->IsInitialized() == false) { - if (GetQA()->InitQA(GetProcessingSettings().runQA ? -GetProcessingSettings().runQA : -1)) { + if (GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : gpudatatypes::gpuqa::tasksAutomatic)) { return 1; } } @@ -1003,3 +1020,16 @@ void GPUChainTracking::SetO2Propagator(const o2::base::Propagator* prop) GPUFatal("GPU magnetic field for propagator requested, but received an O2 propagator without GPU field"); } } + +void GPUChainTracking::ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, gpudatatypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode) +{ + if (syncMode) { + rec.useMatLUT = false; + } + if (proc.rtc.optSpecialCode == -1) { + proc.rtc.optSpecialCode = syncMode; + } + if (dEdxMode != -2) { + steps.setBits(gpudatatypes::RecoStep::TPCdEdx, dEdxMode == -1 ? !syncMode : (dEdxMode > 0)); + } +} diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 8de49cc954e35..fd75136f51d76 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -16,7 +16,8 @@ #define GPUCHAINTRACKING_H #include "GPUChain.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" +#include "GPUDataTypesConfig.h" #include #include #include @@ -58,6 +59,7 @@ class GPUDisplayInterface; class GPUQA; class GPUTPCClusterStatistics; class GPUTRDGeometry; +class GPUTRDRecoParam; class TPCFastTransform; class GPUTrackingInputProvider; struct GPUChainTrackingFinalContext; @@ -66,6 +68,8 @@ struct GPUNewCalibValues; struct GPUTriggerOutputs; struct CfFragment; class GPUTPCClusterFinder; +struct GPUSettingsProcessing; +struct GPUSettingsRec; class GPUChainTracking : public GPUChain { @@ -86,6 +90,7 @@ class GPUChainTracking : public GPUChain void ClearErrorCodes(bool cpuOnly = false); int32_t DoQueuedUpdates(int32_t stream, bool updateSlave = true); // Forces doing queue calib updates, don't call when you are not sure you are allowed to do so! bool QARanForTF() const { return mFractionalQAEnabled; } + static void ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, gpudatatypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode = -2); // Structures for input and output data GPUTrackingInOutPointers& mIOPtrs; @@ -174,13 +179,16 @@ class GPUChainTracking : public GPUChain const o2::tpc::CalibdEdxContainer* GetdEdxCalibContainer() const; const o2::base::MatLayerCylSet* GetMatLUT() const; const GPUTRDGeometry* GetTRDGeometry() const; + const GPUTRDRecoParam* GetTRDRecoParam() const; const o2::base::Propagator* GetO2Propagator() const; const o2::base::Propagator* GetDeviceO2Propagator(); void SetTPCFastTransform(std::unique_ptr&& tpcFastTransform, std::unique_ptr&& tpcTransformHelper); void SetMatLUT(std::unique_ptr&& lut); void SetTRDGeometry(std::unique_ptr&& geo); + void SetTRDRecoParam(std::unique_ptr&& par); void SetMatLUT(const o2::base::MatLayerCylSet* lut); void SetTRDGeometry(const o2::trd::GeometryFlat* geo); + void SetTRDRecoParam(const GPUTRDRecoParam* par); void SetO2Propagator(const o2::base::Propagator* prop); void SetCalibObjects(const GPUCalibObjectsConst& obj); void SetCalibObjects(const GPUCalibObjects& obj); @@ -263,6 +271,7 @@ class GPUChainTracking : public GPUChain std::unique_ptr mdEdxCalibContainerU; // TPC dEdx calibration container std::unique_ptr mMatLUTU; // Material Lookup Table std::unique_ptr mTRDGeometryU; // TRD Geometry + std::unique_ptr mTRDRecoParamU; // TRD RecoParam // Ptrs to internal buffers std::unique_ptr mClusterNativeAccess, mClusterNativeAccessReduced; @@ -306,6 +315,7 @@ class GPUChainTracking : public GPUChain void RunTPCClusterFilter(o2::tpc::ClusterNativeAccess* clusters, std::function allocator, bool applyClusterCuts); bool NeedTPCClustersOnGPU(); void WriteReducedClusters(); + void SortClusters(bool buildNativeGPU, bool propagateMCLabels, o2::tpc::ClusterNativeAccess* clusterAccess, o2::tpc::ClusterNative* clusters); template int32_t RunTRDTrackingInternal(); uint32_t StreamForSector(uint32_t sector) const; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index b0d466f13e5ef..bf6577cfd929e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -47,18 +47,18 @@ #ifdef GPUCA_HAS_ONNX #include "GPUTPCNNClusterizerKernels.h" #include "GPUTPCNNClusterizerHost.h" +#include "ORTRootSerializer.h" #endif #ifdef GPUCA_O2_LIB #include "CommonDataFormat/InteractionRecord.h" #endif +#include "utils/VcShim.h" #include "utils/strtag.h" #include - -#ifndef GPUCA_NO_VC -#include -#endif +#include +#include using namespace o2::gpu; using namespace o2::tpc; @@ -68,7 +68,7 @@ using namespace o2::dataformats; #ifdef GPUCA_TPC_GEOMETRY_O2 std::pair GPUChainTracking::TPCClusterizerDecodeZSCountUpdate(uint32_t iSector, const CfFragment& fragment) { - bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; + bool doGPU = mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding; GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSector]; GPUTPCClusterFinder::ZSOffset* o = processors()->tpcClusterer[iSector].mPzsOffsets; uint32_t digits = 0; @@ -169,11 +169,11 @@ std::pair GPUChainTracking::TPCClusterizerDecodeZSCount(uint uint32_t nPages = 0; uint32_t endpointAdcSamples[GPUTrackingInOutZS::NENDPOINTS]; memset(endpointAdcSamples, 0, sizeof(endpointAdcSamples)); - bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; + bool doGPU = mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding; int32_t firstHBF = (mIOPtrs.settingsTF && mIOPtrs.settingsTF->hasTfStartOrbit) ? mIOPtrs.settingsTF->tfStartOrbit : ((mIOPtrs.tpcZS->sector[iSector].count[0] && mIOPtrs.tpcZS->sector[iSector].nZSPtr[0][0]) ? o2::raw::RDHUtils::getHeartBeatOrbit(*(const o2::header::RAWDataHeader*)mIOPtrs.tpcZS->sector[iSector].zsPtr[0][0]) : 0); for (uint16_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { -#ifndef GPUCA_NO_VC + if (GetProcessingSettings().prefetchTPCpageScan >= 3 && j < GPUTrackingInOutZS::NENDPOINTS - 1) { for (uint32_t k = 0; k < mIOPtrs.tpcZS->sector[iSector].count[j + 1]; k++) { for (uint32_t l = 0; l < mIOPtrs.tpcZS->sector[iSector].nZSPtr[j + 1][k]; l++) { @@ -182,7 +182,6 @@ std::pair GPUChainTracking::TPCClusterizerDecodeZSCount(uint } } } -#endif std::vector> fragments; fragments.reserve(mCFContext->nFragments); @@ -201,12 +200,12 @@ std::pair GPUChainTracking::TPCClusterizerDecodeZSCount(uint } nPages += mIOPtrs.tpcZS->sector[iSector].nZSPtr[j][k]; for (uint32_t l = 0; l < mIOPtrs.tpcZS->sector[iSector].nZSPtr[j][k]; l++) { -#ifndef GPUCA_NO_VC + if (GetProcessingSettings().prefetchTPCpageScan >= 2 && l + 1 < mIOPtrs.tpcZS->sector[iSector].nZSPtr[j][k]) { Vc::Common::prefetchForOneRead(((const uint8_t*)mIOPtrs.tpcZS->sector[iSector].zsPtr[j][k]) + (l + 1) * TPCZSHDR::TPC_ZS_PAGE_SIZE); Vc::Common::prefetchForOneRead(((const uint8_t*)mIOPtrs.tpcZS->sector[iSector].zsPtr[j][k]) + (l + 1) * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(o2::header::RAWDataHeader)); } -#endif + const uint8_t* const page = ((const uint8_t*)mIOPtrs.tpcZS->sector[iSector].zsPtr[j][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE; const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)page; if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) { @@ -476,7 +475,7 @@ std::pair GPUChainTracking::RunTPCClusterizer_transferZS(int int32_t GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) { - bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; + bool doGPU = mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding; if (restorePointers) { for (uint32_t iSector = 0; iSector < NSECTORS; iSector++) { processors()->tpcClusterer[iSector].mPzsOffsets = mCFContext->ptrSave[iSector].zsOffsetHost; @@ -510,7 +509,7 @@ int32_t GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) return 1; } } -#ifndef GPUCA_NO_VC + if (GetProcessingSettings().prefetchTPCpageScan >= 1 && iSector < NSECTORS - 1) { for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { for (uint32_t k = 0; k < mIOPtrs.tpcZS->sector[iSector].count[j]; k++) { @@ -521,7 +520,7 @@ int32_t GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) } } } -#endif + const auto& x = TPCClusterizerDecodeZSCount(iSector, fragmentMax); nDigitsFragmentMax[iSector] = x.first; processors()->tpcClusterer[iSector].mPmemory->counters.nDigits = x.first; @@ -591,7 +590,8 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) return ForwardTPCDigits(); } #ifdef GPUCA_TPC_GEOMETRY_O2 - [[maybe_unused]] int32_t tpcTimeBinCut = mUpdateNewCalibObjects && mNewCalibValues->newTPCTimeBinCut ? mNewCalibValues->tpcTimeBinCut : param().tpcCutTimeBin; + int32_t tpcTimeBinCut = (mUpdateNewCalibObjects && mNewCalibValues->newTPCTimeBinCut) ? mNewCalibValues->tpcTimeBinCut : param().tpcCutTimeBin; + mRec->PushNonPersistentMemory(qStr2Tag("TPCCLUST")); const auto& threadContext = GetThreadContext(); const bool doGPU = GetRecoStepsGPU() & RecoStep::TPCClusterFinding; @@ -643,7 +643,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) // Maximum of 4 lanes supported HighResTimer* nnTimers[12]; - if (GetProcessingSettings().nn.applyNNclusterizer) { + if (nn_settings.applyNNclusterizer) { int32_t deviceId = -1; int32_t numLanes = GetProcessingSettings().nTPCClustererLanes; int32_t maxThreads = mRec->getNKernelHostThreads(true); @@ -681,7 +681,11 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) // nnApplications[lane].directOrtAllocator((nnApplications[lane].mModelClass).getEnv(), (nnApplications[lane].mModelClass).getMemoryInfo(), mRec, recreateMemoryAllocator); // } // recreateMemoryAllocator = true; - (nnApplications[lane].mModelClass).initSession(); + if (!nn_settings.nnLoadFromCCDB) { + (nnApplications[lane].mModelClass).initSession(); // loads from file + } else { + (nnApplications[lane].mModelClass).initSessionFromBuffer((processors()->calibObjects.nnClusterizerNetworks[0])->getONNXModel(), (processors()->calibObjects.nnClusterizerNetworks[0])->getONNXModelSize()); // loads from CCDB + } } if (nnApplications[lane].mModelsUsed[1]) { SetONNXGPUStream(*(nnApplications[lane].mModelReg1).getSessionOptions(), lane, &deviceId); @@ -692,7 +696,11 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) // (nnApplications[lane].mModelReg1).setEnv((nnApplications[lane].mModelClass).getEnv()); (nnApplications[lane].mModelReg1).initEnvironment(); // nnApplications[lane].directOrtAllocator((nnApplications[lane].mModelReg1).getEnv(), (nnApplications[lane].mModelReg1).getMemoryInfo(), mRec, recreateMemoryAllocator); - (nnApplications[lane].mModelReg1).initSession(); + if (!nn_settings.nnLoadFromCCDB) { + (nnApplications[lane].mModelReg1).initSession(); // loads from file + } else { + (nnApplications[lane].mModelReg1).initSessionFromBuffer((processors()->calibObjects.nnClusterizerNetworks[1])->getONNXModel(), (processors()->calibObjects.nnClusterizerNetworks[1])->getONNXModelSize()); // loads from CCDB + } } if (nnApplications[lane].mModelsUsed[2]) { SetONNXGPUStream(*(nnApplications[lane].mModelReg2).getSessionOptions(), lane, &deviceId); @@ -703,7 +711,11 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) // (nnApplications[lane].mModelReg2).setEnv((nnApplications[lane].mModelClass).getEnv()); (nnApplications[lane].mModelReg2).initEnvironment(); // nnApplications[lane].directOrtAllocator((nnApplications[lane].mModelClass).getEnv(), (nnApplications[lane].mModelClass).getMemoryInfo(), mRec, recreateMemoryAllocator); - (nnApplications[lane].mModelReg2).initSession(); + if (!nn_settings.nnLoadFromCCDB) { + (nnApplications[lane].mModelReg2).initSession(); // loads from file + } else { + (nnApplications[lane].mModelReg2).initSessionFromBuffer((processors()->calibObjects.nnClusterizerNetworks[2])->getONNXModel(), (processors()->calibObjects.nnClusterizerNetworks[2])->getONNXModelSize()); // loads from CCDB + } } if (nn_settings.nnClusterizerVerbosity > 0) { LOG(info) << "(ORT) Allocated ONNX stream for lane " << lane << " and device " << deviceId; @@ -752,14 +764,13 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) ClusterNative* tmpNativeClusters = nullptr; std::unique_ptr tmpNativeClusterBuffer; - // setup MC Labels - bool propagateMCLabels = GetProcessingSettings().runMC && processors()->ioPtrs.tpcPackedDigits && processors()->ioPtrs.tpcPackedDigits->tpcDigitsMC; + const bool buildNativeGPU = doGPU && NeedTPCClustersOnGPU(); + const bool buildNativeHost = (mRec->GetRecoStepsOutputs() & gpudatatypes::InOutType::TPCClusters) || GetProcessingSettings().deterministicGPUReconstruction; // TODO: Should do this also when clusters are needed for later steps on the host but not requested as output + const bool propagateMCLabels = buildNativeHost && GetProcessingSettings().runMC && processors()->ioPtrs.tpcPackedDigits && processors()->ioPtrs.tpcPackedDigits->tpcDigitsMC; + const bool sortClusters = buildNativeHost && (GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4); auto* digitsMC = propagateMCLabels ? processors()->ioPtrs.tpcPackedDigits->tpcDigitsMC : nullptr; - bool buildNativeGPU = doGPU && NeedTPCClustersOnGPU(); - bool buildNativeHost = (mRec->GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCClusters) || GetProcessingSettings().deterministicGPUReconstruction; // TODO: Should do this also when clusters are needed for later steps on the host but not requested as output - mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = mRec->MemoryScalers()->nTPCHits * tpcHitLowOccupancyScalingFactor; if (buildNativeGPU) { AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); @@ -902,13 +913,13 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) break; case ZSVersionRowBased10BitADC: case ZSVersionRowBased12BitADC: - runKernel({GetGridBlk(nBlocks, lane), {iSector}}, firstHBF); + runKernel({GetGridBlk(nBlocks, lane), {iSector}}, firstHBF, tpcTimeBinCut); break; case ZSVersionLinkBasedWithMeta: - runKernel({GetGridBlk(nBlocks, lane), {iSector}}, firstHBF); + runKernel({GetGridBlk(nBlocks, lane), {iSector}}, firstHBF, tpcTimeBinCut); break; case ZSVersionDenseLinkBased: - runKernel({GetGridBlk(nBlocks, lane), {iSector}}, firstHBF); + runKernel({GetGridBlk(nBlocks, lane), {iSector}}, firstHBF, tpcTimeBinCut); break; } TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); @@ -951,9 +962,10 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) checkForNoisyPads &= !GetProcessingSettings().disableTPCNoisyPadFilter; if (checkForNoisyPads) { - int32_t nBlocks = TPC_PADS_IN_SECTOR / GPUTPCCFCheckPadBaseline::PadsPerCacheline; + const int32_t nBlocks = GPUTPCCFCheckPadBaseline::GetNBlocks(doGPU); runKernel({GetGridBlk(nBlocks, lane), {iSector}}); + getKernelTimer(RecoStep::TPCClusterFinding, iSector, TPC_PADS_IN_SECTOR * fragment.lengthWithoutOverlap() * sizeof(PackedCharge), false); } runKernel({GetGrid(clusterer.mPmemory->counters.nPositions, lane), {iSector}}); @@ -1035,7 +1047,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) // Filling the data if (mRec->IsGPU() || GetProcessingSettings().nn.nnClusterizerForceGpuInputFill) { // Fills element by element of each input matrix -> better parallelizability, but worse on CPU due to unnecessary computations - runKernel({GetGrid(iSize * clustererNNShadow.mNnClusterizerRowTimeSizeFull, lane), krnlRunRangeNone}, iSector, clustererNNShadow.mNnInferenceInputDType, propagateMCLabels, batchStart); + runKernel({GetGrid(iSize * clustererNNShadow.mNnClusterizerRowTimeSizeThreads , lane), krnlRunRangeNone}, iSector, clustererNNShadow.mNnInferenceInputDType, propagateMCLabels, batchStart); } else { // Fills the whole input matrix at once -> better performance on CPU, but worse parallelizability runKernel({GetGrid(iSize, lane), krnlRunRangeNone}, iSector, clustererNNShadow.mNnInferenceInputDType, propagateMCLabels, batchStart); @@ -1264,22 +1276,26 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) mTriggerBuffer->triggers.clear(); } + // Number of clusters is logged by tracking. This ensures clusters are still printed if it's not running + if (!(GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking)) { + GPUInfo("Event has %zu TPC Clusters", nClsTotal); + } + ClusterNativeAccess::ConstMCLabelContainerView* mcLabelsConstView = nullptr; - if (propagateMCLabels) { - // TODO: write to buffer directly + if (propagateMCLabels) { // TODO: write to buffer directly o2::dataformats::MCTruthContainer mcLabels; std::pair buffer; - if (!GetProcessingSettings().tpcWriteClustersAfterRejection && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)] && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]->useExternal()) { - if (!mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]->allocator) { + auto& labelOutputControl = mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]; + if (!GetProcessingSettings().tpcWriteClustersAfterRejection && !sortClusters && labelOutputControl && labelOutputControl->useExternal()) { + if (!labelOutputControl->allocator) { throw std::runtime_error("Cluster MC Label buffer missing"); } - ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer* container = reinterpret_cast(mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]->allocator(0)); + ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer* container = reinterpret_cast(labelOutputControl->allocator(0)); buffer = {&container->first, &container->second}; } else { mIOMem.clusterNativeMCView = std::make_unique(); mIOMem.clusterNativeMCBuffer = std::make_unique(); - buffer.first = mIOMem.clusterNativeMCBuffer.get(); - buffer.second = mIOMem.clusterNativeMCView.get(); + buffer = {mIOMem.clusterNativeMCBuffer.get(), mIOMem.clusterNativeMCView.get()}; } assert(propagateMCLabels ? mcLinearLabels.header.size() == nClsTotal : true); @@ -1334,15 +1350,8 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (doGPU && synchronizeCalibUpdate) { SynchronizeStream(0); } - if (buildNativeHost && (GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4)) { - for (uint32_t i = 0; i < NSECTORS; i++) { - for (uint32_t j = 0; j < GPUCA_ROW_COUNT; j++) { - std::sort(&tmpNativeClusters[tmpNativeAccess->clusterOffset[i][j]], &tmpNativeClusters[tmpNativeAccess->clusterOffset[i][j] + tmpNativeAccess->nClusters[i][j]]); - } - } - if (buildNativeGPU) { - GPUMemCpy(RecoStep::TPCClusterFinding, (void*)mInputsShadow->mPclusterNativeBuffer, (const void*)tmpNativeClusters, nClsTotal * sizeof(tmpNativeClusters[0]), -1, true); - } + if (sortClusters) { + SortClusters(buildNativeGPU, propagateMCLabels, tmpNativeAccess, tmpNativeClusters); } mRec->MemoryScalers()->nTPCHits = nClsTotal; mRec->PopNonPersistentMemory(RecoStep::TPCClusterFinding, qStr2Tag("TPCCLUST")); @@ -1358,3 +1367,60 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) #endif return 0; } + +void GPUChainTracking::SortClusters(bool buildNativeGPU, bool propagateMCLabels, ClusterNativeAccess* clusterAccess, ClusterNative* clusters) +{ + if (propagateMCLabels) { + std::vector clsOrder(clusterAccess->nClustersTotal); + std::iota(clsOrder.begin(), clsOrder.end(), 0); + std::vector tmpClusters; + for (uint32_t i = 0; i < NSECTORS; i++) { + for (uint32_t j = 0; j < GPUCA_ROW_COUNT; j++) { + const uint32_t offset = clusterAccess->clusterOffset[i][j]; + std::sort(&clsOrder[offset], &clsOrder[offset + clusterAccess->nClusters[i][j]], [&clusters](const uint32_t a, const uint32_t b) { + return clusters[a] < clusters[b]; + }); + tmpClusters.resize(clusterAccess->nClusters[i][j]); + memcpy(tmpClusters.data(), &clusters[offset], clusterAccess->nClusters[i][j] * sizeof(tmpClusters[0])); + for (uint32_t k = 0; k < tmpClusters.size(); k++) { + clusters[offset + k] = tmpClusters[clsOrder[offset + k] - offset]; + } + } + } + tmpClusters.clear(); + + std::pair labelBuffer; + GPUOutputControl* labelOutput = mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]; + std::unique_ptr tmpUniqueContainerView; + std::unique_ptr tmpUniqueContainerBuffer; + if (labelOutput && labelOutput->allocator) { + ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer* labelContainer = reinterpret_cast(labelOutput->allocator(0)); + labelBuffer = {&labelContainer->first, &labelContainer->second}; + } else { + tmpUniqueContainerView = std::move(mIOMem.clusterNativeMCView); + tmpUniqueContainerBuffer = std::move(mIOMem.clusterNativeMCBuffer); + mIOMem.clusterNativeMCView = std::make_unique(); + mIOMem.clusterNativeMCBuffer = std::make_unique(); + labelBuffer = {mIOMem.clusterNativeMCBuffer.get(), mIOMem.clusterNativeMCView.get()}; + } + + o2::dataformats::MCLabelContainer tmpContainer; + for (uint32_t i = 0; i < clusterAccess->nClustersTotal; i++) { + for (const auto& element : clusterAccess->clustersMCTruth->getLabels(clsOrder[i])) { + tmpContainer.addElement(i, element); + } + } + tmpContainer.flatten_to(*labelBuffer.first); + *labelBuffer.second = *labelBuffer.first; + clusterAccess->clustersMCTruth = labelBuffer.second; + } else { + for (uint32_t i = 0; i < NSECTORS; i++) { + for (uint32_t j = 0; j < GPUCA_ROW_COUNT; j++) { + std::sort(&clusters[clusterAccess->clusterOffset[i][j]], &clusters[clusterAccess->clusterOffset[i][j] + clusterAccess->nClusters[i][j]]); + } + } + } + if (buildNativeGPU) { + GPUMemCpy(RecoStep::TPCClusterFinding, (void*)mInputsShadow->mPclusterNativeBuffer, (const void*)clusters, clusterAccess->nClustersTotal * sizeof(clusters[0]), -1, true); + } +} diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index e06699c0918b8..89d47d0e1b86c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -62,8 +62,9 @@ int32_t GPUChainTracking::RunTPCCompression() #ifdef GPUCA_TPC_GEOMETRY_O2 if (mPipelineFinalizationCtx && GetProcessingSettings().doublePipelineClusterizer) { SynchronizeEventAndRelease(mEvents->single); - ((GPUChainTracking*)GetNextChainInQueue())->RunTPCClusterizer_prepare(false); - ((GPUChainTracking*)GetNextChainInQueue())->mCFContext->ptrClusterNativeSave = processorsShadow()->ioPtrs.clustersNative; + auto* foreignChain = (GPUChainTracking*)GetNextChainInQueue(); + foreignChain->RunTPCClusterizer_prepare(false); + foreignChain->mCFContext->ptrClusterNativeSave = processorsShadow()->ioPtrs.clustersNative; } #endif SynchronizeStream(0); @@ -272,10 +273,10 @@ int32_t GPUChainTracking::RunTPCDecompression() CompressedClusters& inputGPUShadow = DecompressorShadow.mInputGPU; if (cmprClsHost.nTracks && cmprClsHost.solenoidBz != -1e6f && cmprClsHost.solenoidBz != param().bzkG) { - throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); + throw std::runtime_error("Configured solenoid Bz " + std::to_string(param().bzkG) + " does not match value used for track model encoding " + std::to_string(cmprClsHost.solenoidBz)); } if (cmprClsHost.nTracks && cmprClsHost.maxTimeBin != -1e6 && cmprClsHost.maxTimeBin != param().continuousMaxTimeBin) { - throw std::runtime_error("Configured max time bin does not match value used for track model encoding"); + throw std::runtime_error("Configured max time bin " + std::to_string(param().continuousMaxTimeBin) + " does not match value used for track model encoding " + std::to_string(cmprClsHost.maxTimeBin)); } int32_t inputStream = 0; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index 15846246bca0a..fd3c03a8b29ec 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -142,8 +142,10 @@ void GPUChainTracking::PrintMemoryStatistics() std::map usageMap; for (int32_t i = 0; i < NSECTORS; i++) { #ifdef GPUCA_TPC_GEOMETRY_O2 - addToMap("TPC Clusterer Sector Peaks", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nPeaks, processors()->tpcClusterer[i].mNMaxPeaks); - addToMap("TPC Clusterer Sector Clusters", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nClusters, processors()->tpcClusterer[i].mNMaxClusters); + if (processors()->tpcClusterer[i].mPmemory) { + addToMap("TPC Clusterer Sector Peaks", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nPeaks, processors()->tpcClusterer[i].mNMaxPeaks); + addToMap("TPC Clusterer Sector Clusters", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nClusters, processors()->tpcClusterer[i].mNMaxClusters); + } #endif addToMap("TPC Sector Start Hits", usageMap, *processors()->tpcTrackers[i].NStartHits(), processors()->tpcTrackers[i].NMaxStartHits()); addToMap("TPC Sector Tracklets", usageMap, *processors()->tpcTrackers[i].NTracklets(), processors()->tpcTrackers[i].NMaxTracklets()); @@ -152,8 +154,10 @@ void GPUChainTracking::PrintMemoryStatistics() addToMap("TPC Sector TrackHits", usageMap, *processors()->tpcTrackers[i].NTrackHits(), processors()->tpcTrackers[i].NMaxTrackHits()); } addToMap("TPC Clusterer Clusters", usageMap, mRec->MemoryScalers()->nTPCHits, mRec->MemoryScalers()->NTPCClusters(mRec->MemoryScalers()->nTPCdigits)); - addToMap("TPC Tracks", usageMap, processors()->tpcMerger.NMergedTracks(), processors()->tpcMerger.NMaxTracks()); - addToMap("TPC TrackHits", usageMap, processors()->tpcMerger.NMergedTrackClusters(), processors()->tpcMerger.NMaxMergedTrackClusters()); + if (processors()->tpcMerger.Memory()) { + addToMap("TPC Tracks", usageMap, processors()->tpcMerger.NMergedTracks(), processors()->tpcMerger.NMaxTracks()); + addToMap("TPC TrackHits", usageMap, processors()->tpcMerger.NMergedTrackClusters(), processors()->tpcMerger.NMaxMergedTrackClusters()); + } if (mRec->GetProcessingSettings().createO2Output) { addToMap("TPC O2 Tracks", usageMap, processors()->tpcMerger.NOutputTracksTPCO2(), processors()->tpcMerger.NOutputTracksTPCO2()); @@ -161,9 +165,11 @@ void GPUChainTracking::PrintMemoryStatistics() } #ifdef GPUCA_TPC_GEOMETRY_O2 - addToMap("TPC ComprCache HitsAttached", usageMap, processors()->tpcCompressor.mOutput->nAttachedClusters, processors()->tpcCompressor.mMaxTrackClusters); - addToMap("TPC ComprCache HitsUnattached", usageMap, processors()->tpcCompressor.mOutput->nUnattachedClusters, processors()->tpcCompressor.mMaxClustersInCache); - addToMap("TPC ComprCache Tracks", usageMap, processors()->tpcCompressor.mOutput->nTracks, processors()->tpcCompressor.mMaxTracks); + if (processors()->tpcCompressor.mOutput) { + addToMap("TPC ComprCache HitsAttached", usageMap, processors()->tpcCompressor.mOutput->nAttachedClusters, processors()->tpcCompressor.mMaxTrackClusters); + addToMap("TPC ComprCache HitsUnattached", usageMap, processors()->tpcCompressor.mOutput->nUnattachedClusters, processors()->tpcCompressor.mMaxClustersInCache); + addToMap("TPC ComprCache Tracks", usageMap, processors()->tpcCompressor.mOutput->nTracks, processors()->tpcCompressor.mMaxTracks); + } #endif for (auto& elem : usageMap) { @@ -180,8 +186,10 @@ void GPUChainTracking::PrintMemoryRelations() GPUInfo("MEMREL SectorTracks NCl %d NTrk %d", processors()->tpcTrackers[i].NHitsTotal(), *processors()->tpcTrackers[i].NTracks()); GPUInfo("MEMREL SectorTrackHits NCl %d NTrkH %d", processors()->tpcTrackers[i].NHitsTotal(), *processors()->tpcTrackers[i].NTrackHits()); } - GPUInfo("MEMREL Tracks NCl %d NTrk %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTracks()); - GPUInfo("MEMREL TrackHitss NCl %d NTrkH %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTrackClusters()); + if (processors()->tpcMerger.Memory()) { + GPUInfo("MEMREL Tracks NCl %d NTrk %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTracks()); + GPUInfo("MEMREL TrackHitss NCl %d NTrkH %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTrackClusters()); + } } void GPUChainTracking::PrepareKernelDebugOutput() @@ -232,7 +240,7 @@ void GPUChainTracking::PrintOutputStat() } char trdText[1024] = ""; - if (GetRecoSteps() & GPUDataTypes::RecoStep::TRDTracking) { + if (GetRecoSteps() & gpudatatypes::RecoStep::TRDTracking) { int32_t nTRDTracks = 0; int32_t nTRDTracklets = 0; for (uint32_t k = 0; k < mIOPtrs.nTRDTracks; k++) { diff --git a/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc b/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc index 5b72a8f23c242..b3b1773ec664e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc +++ b/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc @@ -26,9 +26,11 @@ inline const TPCZSLinkMapping* GPUChainTracking::GetTPCZSLinkMapping() const { r inline const o2::tpc::CalibdEdxContainer* GPUChainTracking::GetdEdxCalibContainer() const { return processors()->calibObjects.dEdxCalibContainer; } inline const o2::base::MatLayerCylSet* GPUChainTracking::GetMatLUT() const { return processors()->calibObjects.matLUT; } inline const GPUTRDGeometry* GPUChainTracking::GetTRDGeometry() const { return (GPUTRDGeometry*)processors()->calibObjects.trdGeometry; } +inline const GPUTRDRecoParam* GPUChainTracking::GetTRDRecoParam() const { return processors()->calibObjects.trdRecoParam; } inline const o2::base::Propagator* GPUChainTracking::GetO2Propagator() const { return processors()->calibObjects.o2Propagator; } inline void GPUChainTracking::SetMatLUT(const o2::base::MatLayerCylSet* lut) { processors()->calibObjects.matLUT = lut; } inline void GPUChainTracking::SetTRDGeometry(const o2::trd::GeometryFlat* geo) { processors()->calibObjects.trdGeometry = geo; } +inline void GPUChainTracking::SetTRDRecoParam(const GPUTRDRecoParam* par) { processors()->calibObjects.trdRecoParam = par; } inline void GPUChainTracking::SetCalibObjects(const GPUCalibObjectsConst& obj) { processors()->calibObjects = obj; } inline void GPUChainTracking::SetCalibObjects(const GPUCalibObjects& obj) { memcpy((void*)&processors()->calibObjects, (const void*)&obj, sizeof(obj)); } } // namespace o2::gpu diff --git a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx index dd11e9989f684..6f24415564a8c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx @@ -20,6 +20,7 @@ #include "GPUTPCTrack.h" #include "GPUTPCHitId.h" #include "GPUTRDTrackletWord.h" +#include "GPUTRDRecoParam.h" #include "AliHLTTPCClusterMCData.h" #include "GPUTPCMCInfo.h" #include "GPUTRDTrack.h" @@ -337,6 +338,11 @@ void GPUChainTracking::DumpSettings(const char* dir) f += "trdgeometry.dump"; DumpStructToFile(processors()->calibObjects.trdGeometry, f.c_str()); } + if (processors()->calibObjects.trdRecoParam != nullptr) { + f = dir; + f += "trdrecoparam.dump"; + DumpStructToFile(processors()->calibObjects.trdRecoParam, f.c_str()); + } } void GPUChainTracking::ReadSettings(const char* dir) @@ -382,4 +388,8 @@ void GPUChainTracking::ReadSettings(const char* dir) f += "trdgeometry.dump"; mTRDGeometryU = ReadStructFromFile(f.c_str()); processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); + f = dir; + f += "trdrecoparam.dump"; + mTRDRecoParamU = ReadStructFromFile(f.c_str()); + processors()->calibObjects.trdRecoParam = mTRDRecoParamU.get(); } diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx index d13e8d5544631..e2d68f10819fb 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx @@ -100,6 +100,7 @@ int32_t GPUChainTracking::RunTPCTrackingSectors_internal() bool streamInit[GPUCA_MAX_STREAMS] = {false}; int32_t streamInitAndOccMap = mRec->NStreams() - 1; + bool initializeOccMap = param().rec.tpc.occupancyMapTimeBins || param().rec.tpc.sysClusErrorC12Norm; if (doGPU) { // Copy Tracker Object to GPU Memory if (GetProcessingSettings().debugLevel >= 3) { @@ -109,7 +110,7 @@ int32_t GPUChainTracking::RunTPCTrackingSectors_internal() return 2; } - WriteToConstantMemory(RecoStep::TPCSectorTracking, (char*)processors()->tpcTrackers - (char*)processors(), processorsShadow()->tpcTrackers, sizeof(GPUTPCTracker) * NSECTORS, streamInitAndOccMap, &mEvents->init); + WriteToConstantMemory(RecoStep::TPCSectorTracking, (char*)processors()->tpcTrackers - (char*)processors(), processorsShadow()->tpcTrackers, sizeof(GPUTPCTracker) * NSECTORS, streamInitAndOccMap, !initializeOccMap ? &mEvents->init : nullptr); std::fill(streamInit, streamInit + mRec->NStreams(), false); streamInit[streamInitAndOccMap] = true; @@ -126,19 +127,19 @@ int32_t GPUChainTracking::RunTPCTrackingSectors_internal() auto* ptrTmp = (GPUTPCClusterOccupancyMapBin*)mRec->AllocateVolatileMemory(GPUTPCClusterOccupancyMapBin::getTotalSize(param()), doGPU); runKernel(GetGridAutoStep(streamInitAndOccMap, RecoStep::TPCSectorTracking), ptrTmp, GPUTPCClusterOccupancyMapBin::getTotalSize(param())); runKernel(GetGridBlk(GPUCA_NSECTORS * GPUCA_ROW_COUNT, streamInitAndOccMap), ptrTmp); - runKernel(GetGridBlk(GPUTPCClusterOccupancyMapBin::getNBins(param()), streamInitAndOccMap), ptrTmp, ptr + 2); + runKernel(GetGridBlk(mInputsHost->mTPCClusterOccupancyMapSize, streamInitAndOccMap), ptrTmp, ptr + 2); mRec->ReturnVolatileMemory(); mInputsHost->mTPCClusterOccupancyMap[1] = param().rec.tpc.occupancyMapTimeBins * 0x10000 + param().rec.tpc.occupancyMapTimeBinsAverage; if (doGPU) { - GPUMemCpy(RecoStep::TPCSectorTracking, mInputsHost->mTPCClusterOccupancyMap + 2, mInputsShadow->mTPCClusterOccupancyMap + 2, sizeof(*ptr) * GPUTPCClusterOccupancyMapBin::getNBins(mRec->GetParam()), streamInitAndOccMap, false, &mEvents->init); + GPUMemCpy(RecoStep::TPCSectorTracking, mInputsHost->mTPCClusterOccupancyMap + 2, mInputsShadow->mTPCClusterOccupancyMap + 2, sizeof(*ptr) * mInputsHost->mTPCClusterOccupancyMapSize, streamInitAndOccMap, false); } else { - TransferMemoryResourceLinkToGPU(RecoStep::TPCSectorTracking, mInputsHost->mResourceOccupancyMap, streamInitAndOccMap, &mEvents->init); + TransferMemoryResourceLinkToGPU(RecoStep::TPCSectorTracking, mInputsHost->mResourceOccupancyMap, streamInitAndOccMap); } } - if (param().rec.tpc.occupancyMapTimeBins || param().rec.tpc.sysClusErrorC12Norm) { + if (initializeOccMap) { uint32_t& occupancyTotal = *mInputsHost->mTPCClusterOccupancyMap; occupancyTotal = CAMath::Float2UIntRn(mRec->MemoryScalers()->nTPCHits / (mIOPtrs.settingsTF && mIOPtrs.settingsTF->hasNHBFPerTF ? mIOPtrs.settingsTF->nHBFPerTF : 128)); - mRec->UpdateParamOccupancyMap(param().rec.tpc.occupancyMapTimeBins ? mInputsHost->mTPCClusterOccupancyMap + 2 : nullptr, doGPU && param().rec.tpc.occupancyMapTimeBins ? mInputsShadow->mTPCClusterOccupancyMap + 2 : nullptr, occupancyTotal, streamInitAndOccMap); + mRec->UpdateParamOccupancyMap(param().rec.tpc.occupancyMapTimeBins ? mInputsHost->mTPCClusterOccupancyMap + 2 : nullptr, doGPU && param().rec.tpc.occupancyMapTimeBins ? mInputsShadow->mTPCClusterOccupancyMap + 2 : nullptr, occupancyTotal, mInputsHost->mTPCClusterOccupancyMapSize, streamInitAndOccMap, &mEvents->init); } int32_t streamMap[NSECTORS]; @@ -148,9 +149,6 @@ int32_t GPUChainTracking::RunTPCTrackingSectors_internal() GPUTPCTracker& trk = processors()->tpcTrackers[iSector]; GPUTPCTracker& trkShadow = doGPU ? processorsShadow()->tpcTrackers[iSector] : trk; int32_t useStream = StreamForSector(iSector); - if (GetProcessingSettings().amdMI100SerializationWorkaround) { - SynchronizeStream(useStream); // TODO: Remove this workaround once fixed on MI100 - } if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Creating Sector Data (Sector %d)", iSector); @@ -224,6 +222,9 @@ int32_t GPUChainTracking::RunTPCTrackingSectors_internal() GPUInfo("Sector %u, Number of tracks: %d", iSector, *trk.NTracks()); } DoDebugAndDump(RecoStep::TPCSectorTracking, GPUChainTrackingDebugFlags::TPCSectorTracks, trk, &GPUTPCTracker::DumpTrackHits, *mDebugFile); + if (GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL && !trk.MemoryReuseAllowed()) { + mRec->PopNonPersistentMemory(RecoStep::TPCSectorTracking, qStr2Tag("TPCSLTRK"), &trk); + } }); mRec->SetNActiveThreadsOuterLoop(1); if (error) { diff --git a/GPU/GPUTracking/Global/GPUChainTrackingTransformation.cxx b/GPU/GPUTracking/Global/GPUChainTrackingTransformation.cxx index 83ddc45830621..8b36c9520659e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingTransformation.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingTransformation.cxx @@ -32,7 +32,7 @@ using namespace o2::tpc; bool GPUChainTracking::NeedTPCClustersOnGPU() { - return (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCConversion) || (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking) || (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging) || (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression); + return (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCConversion) || (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCSectorTracking) || (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCMerging) || (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCCompression); } int32_t GPUChainTracking::ConvertNativeToClusterData() @@ -41,7 +41,7 @@ int32_t GPUChainTracking::ConvertNativeToClusterData() const auto& threadContext = GetThreadContext(); bool transferClusters = false; - if (mRec->IsGPU() && !(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding) && NeedTPCClustersOnGPU()) { + if (mRec->IsGPU() && !(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding) && NeedTPCClustersOnGPU()) { mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = mIOPtrs.clustersNative->nClustersTotal; AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); processorsShadow()->ioPtrs.clustersNative = mInputsShadow->mPclusterNativeAccess; diff --git a/GPU/GPUTracking/Global/GPUErrorCodes.h b/GPU/GPUTracking/Global/GPUErrorCodes.h index 8fec23be00a09..a4921f478b107 100644 --- a/GPU/GPUTracking/Global/GPUErrorCodes.h +++ b/GPU/GPUTracking/Global/GPUErrorCodes.h @@ -47,6 +47,10 @@ GPUCA_ERROR_CODE(26, ERROR_TPCZS_INVALID_ROW, SectorRow) GPUCA_ERROR_CODE(27, ERROR_TPCZS_INVALID_NADC, SectorCRU, SamplesInPage, SamplesWritten) // Invalid number of ADC samples in header, existing samples were decoded GPUCA_ERROR_CODE(28, ERROR_TPCZS_INCOMPLETE_HBF, SectorCRU, PacketCount, NextPacketCount) // Part of HBF is missing, decoding incomplete GPUCA_ERROR_CODE(29, ERROR_TPCZS_INVALID_OFFSET, SectorEndpoint, Value, Expected) // Raw page is skipped since it contains invalid payload offset -GPUCA_ERROR_CODE(29, MAX_GPUCA_ERROR_NUMBER) +GPUCA_ERROR_CODE(30, ERROR_TPCZS_INVALID_MAGIC_WORD, Value) // ZS header contains wrong magic word +GPUCA_ERROR_CODE(31, ERROR_TPCZS_PAGE_OVERFLOW, Position, PageEnd) // Ran out of page to decode +GPUCA_ERROR_CODE(32, ERROR_TPCZS_VERSION_MISMATCH, Value, Expected) // ZS decoder received page with wrong version +GPUCA_ERROR_CODE(33, ERROR_TPCZS_UNKNOWN, ErrorCode) // Unkown or invalid error code raised in decoder +GPUCA_ERROR_CODE(33, MAX_GPUCA_ERROR_NUMBER) // #define GPUCA_CHECK_TPCZS_CORRUPTION diff --git a/GPU/GPUTracking/Global/GPUErrors.cxx b/GPU/GPUTracking/Global/GPUErrors.cxx index 4baa299c6b976..dfe7a84f0f179 100644 --- a/GPU/GPUTracking/Global/GPUErrors.cxx +++ b/GPU/GPUTracking/Global/GPUErrors.cxx @@ -13,7 +13,7 @@ /// \author David Rohr #include "GPUErrors.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUCommonMath.h" #include "GPUDefMacros.h" #include "GPULogging.h" diff --git a/GPU/GPUTracking/Global/GPUTrackingInputProvider.cxx b/GPU/GPUTracking/Global/GPUTrackingInputProvider.cxx index 7ef9baa903fbe..dc47b6c0a6663 100644 --- a/GPU/GPUTracking/Global/GPUTrackingInputProvider.cxx +++ b/GPU/GPUTracking/Global/GPUTrackingInputProvider.cxx @@ -13,7 +13,7 @@ /// \author David Rohr #include "GPUTrackingInputProvider.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTRDTrackletWord.h" #include "GPUReconstruction.h" #include "GPUTPCClusterOccupancyMap.h" @@ -28,7 +28,7 @@ using namespace o2::tpc; void GPUTrackingInputProvider::InitializeProcessor() {} void* GPUTrackingInputProvider::SetPointersInputZS(void* mem) { - if (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding) { + if (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding) { computePointerWithAlignment(mem, mPzsMeta); computePointerWithAlignment(mem, mPzsSizes, GPUTrackingInOutZS::NSECTORS * GPUTrackingInOutZS::NENDPOINTS); computePointerWithAlignment(mem, mPzsPtrs, GPUTrackingInOutZS::NSECTORS * GPUTrackingInOutZS::NENDPOINTS); @@ -82,7 +82,8 @@ void* GPUTrackingInputProvider::SetPointersInputTRD(void* mem) void* GPUTrackingInputProvider::SetPointersTPCOccupancyMap(void* mem) { if (mHoldTPCOccupancyMap) { - computePointerWithAlignment(mem, mTPCClusterOccupancyMap, (mRec->GetParam().rec.tpc.occupancyMapTimeBins ? GPUTPCClusterOccupancyMapBin::getNBins(mRec->GetParam()) + 1 : 0) + 1); // +1 for total occupancy estimator, +1 for sanity check information + mTPCClusterOccupancyMapSize = mRec->GetParam().rec.tpc.occupancyMapTimeBins ? GPUTPCClusterOccupancyMapBin::getNBins(mRec->GetParam()) : 0; + computePointerWithAlignment(mem, mTPCClusterOccupancyMap, (mRec->GetParam().rec.tpc.occupancyMapTimeBins ? mTPCClusterOccupancyMapSize + 1 : 0) + 1); // +1 for total occupancy estimator, +1 for sanity check information } return mem; } @@ -100,7 +101,7 @@ void GPUTrackingInputProvider::RegisterMemoryAllocation() void GPUTrackingInputProvider::SetMaxData(const GPUTrackingInOutPointers& io) { - mHoldTPCZS = io.tpcZS && (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding); + mHoldTPCZS = io.tpcZS && (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding); mHoldTPCClusterNative = (io.tpcZS || io.tpcPackedDigits || io.clustersNative || io.tpcCompressedClusters) && (mRec->IsGPU() || io.tpcCompressedClusters); mHoldTPCOccupancyMap = (io.tpcZS || io.tpcPackedDigits || io.clustersNative || io.tpcCompressedClusters) && (mRec->GetParam().rec.tpc.occupancyMapTimeBins || mRec->GetParam().rec.tpc.sysClusErrorC12Norm); mHoldTPCClusterNativeOutput = io.tpcZS || io.tpcPackedDigits || io.tpcCompressedClusters; diff --git a/GPU/GPUTracking/Global/GPUTrackingInputProvider.h b/GPU/GPUTracking/Global/GPUTrackingInputProvider.h index 910e87fd02126..7aee803a03ace 100644 --- a/GPU/GPUTracking/Global/GPUTrackingInputProvider.h +++ b/GPU/GPUTracking/Global/GPUTrackingInputProvider.h @@ -81,6 +81,7 @@ class GPUTrackingInputProvider : public GPUProcessor o2::tpc::ClusterNative* mPclusterNativeOutput = nullptr; uint32_t* mTPCClusterOccupancyMap = nullptr; + uint32_t mTPCClusterOccupancyMapSize = 0; uint32_t* mErrorCodes = nullptr; }; diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.cxx b/GPU/GPUTracking/Interface/GPUO2Interface.cxx index 65907528a3dba..ced3016dc15b1 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.cxx +++ b/GPU/GPUTracking/Interface/GPUO2Interface.cxx @@ -60,10 +60,10 @@ int32_t GPUO2Interface::Initialize(const GPUO2InterfaceConfiguration& config) mConfig.reset(new GPUO2InterfaceConfiguration(config)); mNContexts = mConfig->configProcessing.doublePipeline ? 2 : 1; mCtx.reset(new GPUO2Interface_processingContext[mNContexts]); - if (mConfig->configWorkflow.inputs.isSet(GPUDataTypes::InOutType::TPCRaw)) { + if (mConfig->configWorkflow.inputs.isSet(gpudatatypes::InOutType::TPCRaw)) { mConfig->configGRP.needsClusterer = 1; } - if (mConfig->configWorkflow.inputs.isSet(GPUDataTypes::InOutType::TPCCompressedClusters)) { + if (mConfig->configWorkflow.inputs.isSet(gpudatatypes::InOutType::TPCCompressedClusters)) { mConfig->configGRP.doCompClusterDecode = 1; } for (uint32_t i = 0; i < mNContexts; i++) { @@ -89,7 +89,7 @@ int32_t GPUO2Interface::Initialize(const GPUO2InterfaceConfiguration& config) mCtx[i].mRec->SetSettings(&mConfig->configGRP, &mConfig->configReconstruction, &mConfig->configProcessing, &mConfig->configWorkflow); mCtx[i].mChain->SetCalibObjects(mConfig->configCalib); - if (i == 0 && mConfig->configWorkflow.steps.isSet(GPUDataTypes::RecoStep::ITSTracking)) { + if (i == 0 && mConfig->configWorkflow.steps.isSet(gpudatatypes::RecoStep::ITSTracking)) { mChainITS = mCtx[i].mRec->AddChain(); } @@ -137,29 +137,30 @@ void GPUO2Interface::Deinitialize() mNContexts = 0; } -void GPUO2Interface::DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data) +void GPUO2Interface::DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data, uint32_t iThread, const char* dir) { - mCtx[0].mChain->ClearIOPointers(); - mCtx[0].mChain->mIOPtrs = *data; + const auto oldPtrs = mCtx[iThread].mChain->mIOPtrs; + mCtx[iThread].mChain->mIOPtrs = *data; char fname[1024]; - snprintf(fname, 1024, "event.%d.dump", nEvent); - mCtx[0].mChain->DumpData(fname); + snprintf(fname, 1024, "%sevent.%d.dump", dir, nEvent); + mCtx[iThread].mChain->DumpData(fname); if (nEvent == 0) { #ifdef GPUCA_BUILD_QA if (mConfig->configProcessing.runMC) { - mCtx[0].mChain->ForceInitQA(); + mCtx[iThread].mChain->ForceInitQA(); snprintf(fname, 1024, "mc.%d.dump", nEvent); - mCtx[0].mChain->GetQA()->UpdateChain(mCtx[0].mChain); - mCtx[0].mChain->GetQA()->DumpO2MCData(fname); + mCtx[iThread].mChain->GetQA()->UpdateChain(mCtx[iThread].mChain); + mCtx[iThread].mChain->GetQA()->DumpO2MCData(fname); } #endif } + mCtx[iThread].mChain->mIOPtrs = oldPtrs; } -void GPUO2Interface::DumpSettings() +void GPUO2Interface::DumpSettings(uint32_t iThread, const char* dir) { - mCtx[0].mChain->DoQueuedUpdates(-1); - mCtx[0].mRec->DumpSettings(); + mCtx[iThread].mChain->DoQueuedUpdates(-1); + mCtx[iThread].mRec->DumpSettings(dir); } int32_t GPUO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs, uint32_t iThread, GPUInterfaceInputUpdate* inputUpdateCallback) @@ -267,3 +268,8 @@ void GPUO2Interface::UseGPUPolynomialFieldInPropagator(o2::base::Propagator* pro { prop->setGPUField(&mCtx[0].mRec->GetParam().polynomialField); } + +void GPUO2Interface::ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, gpudatatypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode) +{ + GPUChainTracking::ApplySyncSettings(proc, rec, steps, syncMode, dEdxMode); +} diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.h b/GPU/GPUTracking/Interface/GPUO2Interface.h index 9b7390f2ed663..ca56018908b41 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.h +++ b/GPU/GPUTracking/Interface/GPUO2Interface.h @@ -15,19 +15,14 @@ #ifndef GPUO2INTERFACE_H #define GPUO2INTERFACE_H -// Some defines denoting that we are compiling for O2 -#ifndef GPUCA_TPC_GEOMETRY_O2 -#define GPUCA_TPC_GEOMETRY_O2 -#endif -#ifndef GPUCA_O2_INTERFACE -#define GPUCA_O2_INTERFACE -#endif +#include "GPUO2ExternalUser.h" +#include "GPUCommonDef.h" +#include "GPUDataTypesIO.h" +#include "GPUDataTypesConfig.h" #include #include #include -#include "GPUCommonDef.h" -#include "GPUDataTypes.h" namespace o2::base { @@ -62,6 +57,8 @@ struct GPUInterfaceInputUpdate; struct GPUTrackingOutputs; struct GPUConstantMem; struct GPUNewCalibValues; +struct GPUSettingsProcessing; +struct GPUSettingsRec; struct GPUO2Interface_processingContext; struct GPUO2Interface_Internals; @@ -77,8 +74,8 @@ class GPUO2Interface int32_t RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs = nullptr, uint32_t iThread = 0, GPUInterfaceInputUpdate* inputUpdateCallback = nullptr); void Clear(bool clearOutputs, uint32_t iThread = 0); - void DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data); - void DumpSettings(); + void DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data, uint32_t iThread, const char* dir = ""); + void DumpSettings(uint32_t iThread, const char* dir = ""); void GetITSTraits(o2::its::TrackerTraits<7>*& trackerTraits, o2::its::VertexerTraits<7>*& vertexerTraits, o2::its::TimeFrame<7>*& timeFrame); const o2::base::Propagator* GetDeviceO2Propagator(int32_t iThread = 0) const; @@ -86,6 +83,7 @@ class GPUO2Interface // Updates all calibration objects that are != nullptr in newCalib int32_t UpdateCalibration(const GPUCalibObjectsConst& newCalib, const GPUNewCalibValues& newVals, uint32_t iThread = 0); + static void ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, gpudatatypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode = -2); int32_t registerMemoryForGPU(const void* ptr, size_t size); int32_t unregisterMemoryForGPU(const void* ptr); diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.cxx index 54477f550b3d4..606f2bfc829e5 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.cxx @@ -13,7 +13,7 @@ /// \author David Rohr #include "GPUO2InterfaceConfiguration.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" using namespace o2::gpu; diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h index af597fc4bf6b9..0f8a3784f0a88 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h @@ -15,23 +15,17 @@ #ifndef GPUO2INTERFACECONFIGURATION_H #define GPUO2INTERFACECONFIGURATION_H -#ifndef GPUCA_TPC_GEOMETRY_O2 -#define GPUCA_TPC_GEOMETRY_O2 -#endif -#ifndef GPUCA_O2_INTERFACE -#define GPUCA_O2_INTERFACE -#endif - -#include -#include -#include -#include -#include +#include "GPUO2ExternalUser.h" #include "GPUSettings.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUHostDataTypes.h" #include "GPUOutputControl.h" #include "DataFormatsTPC/Constants.h" +#include +#include +#include +#include +#include // TODO: Get rid of gsl class TH1F; class TH1D; diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h b/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h index e6d9cb76aed79..c0946bab0076d 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h @@ -15,17 +15,10 @@ #ifndef GPUO2INTERFACEDisplay_H #define GPUO2INTERFACEDisplay_H -// Some defines denoting that we are compiling for O2 -#ifndef GPUCA_TPC_GEOMETRY_O2 -#define GPUCA_TPC_GEOMETRY_O2 -#endif -#ifndef GPUCA_O2_INTERFACE -#define GPUCA_O2_INTERFACE -#endif - +#include "GPUO2ExternalUser.h" #include #include -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" namespace o2::gpu { diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h index 18af81b6cedc7..3fc4cda0a1dcc 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h @@ -15,14 +15,7 @@ #ifndef GPUO2INTERFACEQA_H #define GPUO2INTERFACEQA_H -// Some defines denoting that we are compiling for O2 -#ifndef GPUCA_TPC_GEOMETRY_O2 -#define GPUCA_TPC_GEOMETRY_O2 -#endif -#ifndef GPUCA_O2_INTERFACE -#define GPUCA_O2_INTERFACE -#endif - +#include "GPUO2ExternalUser.h" #include #include diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h index 9f743c89d5743..d4aa6900e308a 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h @@ -15,14 +15,7 @@ #ifndef GPUO2INTERFACEREFIT_H #define GPUO2INTERFACEREFIT_H -// Some defines denoting that we are compiling for O2 -#ifndef GPUCA_TPC_GEOMETRY_O2 -#define GPUCA_TPC_GEOMETRY_O2 -#endif -#ifndef GPUCA_O2_INTERFACE -#define GPUCA_O2_INTERFACE -#endif - +#include "GPUO2ExternalUser.h" #include #include #include diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx index 9454c9a2389ae..43b8dc21eaf15 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx @@ -26,6 +26,7 @@ #include "TPCBase/CRU.h" #include "TPCBase/RDHUtils.h" #include "DataFormatsTPC/ZeroSuppression.h" +#include "GPUTPCClusterOccupancyMap.h" #include using namespace o2::gpu; @@ -132,6 +133,7 @@ void GPUO2InterfaceUtils::paramUseExternalOccupancyMap(GPUParam* param, uint32_t param->occupancyTotal = *occupancymap; if (param->rec.tpc.occupancyMapTimeBins) { param->occupancyMap = occupancymap + 2; + param->occupancyMapSize = GPUTPCClusterOccupancyMapBin::getNBins(*param); } } } diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 9a4b129f751a4..3622e51bd663f 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -92,6 +92,9 @@ struct GPUTPCGMMergerSortTracks_comp { { const GPUTPCGMMergedTrack& GPUrestrict() a = mCmp[aa]; const GPUTPCGMMergedTrack& GPUrestrict() b = mCmp[bb]; + if (a.OK() != b.OK()) { + return a.OK(); + } if (a.CCE() != b.CCE()) { return a.CCE() > b.CCE(); } @@ -417,7 +420,7 @@ void* GPUTPCGMMerger::SetPointersMemory(void* mem) void* GPUTPCGMMerger::SetPointersRefitScratch(void* mem) { computePointerWithAlignment(mem, mTrackOrderAttach, mNMaxTracks); - const bool mergerSortTracks = mRec->GetProcessingSettings().mergerSortTracks == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging).par_SORT_BEFORE_FIT : mRec->GetProcessingSettings().mergerSortTracks; + const bool mergerSortTracks = mRec->GetProcessingSettings().mergerSortTracks == -1 ? mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCMerging).par_SORT_BEFORE_FIT : mRec->GetProcessingSettings().mergerSortTracks; if (mergerSortTracks) { computePointerWithAlignment(mem, mTrackOrderProcess, mNMaxTracks); } @@ -440,7 +443,7 @@ void* GPUTPCGMMerger::SetPointersOutput(void* mem) void* GPUTPCGMMerger::SetPointersOutputState(void* mem) { - if ((mRec->GetRecoSteps() & GPUDataTypes::RecoStep::Refit) || mRec->GetProcessingSettings().outputSharedClusterMap) { + if ((mRec->GetRecoSteps() & gpudatatypes::RecoStep::Refit) || mRec->GetProcessingSettings().outputSharedClusterMap) { computePointerWithAlignment(mem, mClusterStateExt, mNMaxClusters); } else { mClusterStateExt = nullptr; @@ -512,7 +515,7 @@ void GPUTPCGMMerger::SetMaxData(const GPUTrackingInOutPointers& io) } if (io.clustersNative) { mNMaxClusters = io.clustersNative->nClustersTotal; - } else if (mRec->GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking) { + } else if (mRec->GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking) { mNMaxClusters = 0; for (int32_t i = 0; i < NSECTORS; i++) { mNMaxClusters += mRec->GetConstantMem().tpcTrackers[i].NHitsTotal(); @@ -530,7 +533,7 @@ int32_t GPUTPCGMMerger::CheckSectors() throw std::runtime_error("mNMaxSingleSectorTracks too small"); } } - if (!(mRec->GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking)) { + if (!(mRec->GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking)) { throw std::runtime_error("Must run also sector tracking"); } return 0; @@ -552,7 +555,7 @@ GPUd() int32_t GPUTPCGMMerger::RefitSectorTrack(GPUTPCGMSectorTrack& sectorTrack prop.SetMaterialTPC(); prop.SetMaxSinPhi(GPUCA_MAX_SIN_PHI); prop.SetSeedingErrors(true); // Larger errors for seeds, better since we don't start with good hypothesis - prop.SetFitInProjections(false); + prop.SetFitInProjections(true); // TODO: Was false, consider reenabling after fitInProjection is fixed prop.SetPolynomialField(&Param().polynomialField); GPUTPCGMTrackParam trk; trk.X() = inTrack->Param().GetX(); @@ -561,7 +564,7 @@ GPUd() int32_t GPUTPCGMMerger::RefitSectorTrack(GPUTPCGMSectorTrack& sectorTrack trk.SinPhi() = inTrack->Param().GetSinPhi(); trk.DzDs() = inTrack->Param().GetDzDs(); trk.QPt() = inTrack->Param().GetQPt(); - trk.TOffset() = GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, inTrack->Param().GetZOffset(), Param().continuousMaxTimeBin); + trk.TOffset() = Param().par.continuousTracking ? GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, inTrack->Param().GetZOffset(), Param().continuousMaxTimeBin) : 0; const auto tmp = sectorTrack.ClusterTN() > sectorTrack.ClusterT0() ? std::array{sectorTrack.ClusterTN(), sectorTrack.ClusterT0()} : std::array{sectorTrack.ClusterT0(), sectorTrack.ClusterTN()}; trk.ShiftZ(this, sector, tmp[0], tmp[1], inTrack->Param().GetX()); // We do not store the inner / outer cluster X, so we just use the track X instead sectorTrack.SetX2(0.f); @@ -715,9 +718,6 @@ GPUd() void GPUTPCGMMerger::MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nT } else if (iBorder == 3) { // transport to the middle of the sector and rotate vertically to the border on the right dAlpha = -dAlpha; x0 = GPUTPCGeometry::Row2X(63); - } else if (iBorder == 4) { // transport to the middle of the sßector, w/o rotation - dAlpha = 0; - x0 = GPUTPCGeometry::Row2X(63); } const float maxSin = CAMath::Sin(60.f / 180.f * CAMath::Pi()); @@ -780,14 +780,14 @@ GPUd() void GPUTPCGMMerger::MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nT } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { CADEBUG(GPUInfo("\nMERGING Sectors %d %d NTracks %d %d CROSS %d", iSector1, iSector2, N1, N2, mergeMode)); GPUTPCGMBorderRange* range1 = mBorderRange[iSector1]; GPUTPCGMBorderRange* range2 = mBorderRange[iSector2] + *GetConstantMem()->tpcTrackers[iSector2].NTracks(); bool sameSector = (iSector1 == iSector2); for (int32_t itr = iBlock * nThreads + iThread; itr < N1; itr += nThreads * nBlocks) { - GPUTPCGMBorderTrack& b = B1[itr]; + const GPUTPCGMBorderTrack& b = B1[itr]; float d = CAMath::Max(0.5f, 3.5f * CAMath::Sqrt(b.Cov()[1])); if (CAMath::Abs(b.Par()[4]) * Param().qptB5Scaler >= 20) { d *= 2; @@ -806,7 +806,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThrea } if (!sameSector) { for (int32_t itr = iBlock * nThreads + iThread; itr < N2; itr += nThreads * nBlocks) { - GPUTPCGMBorderTrack& b = B2[itr]; + const GPUTPCGMBorderTrack& b = B2[itr]; float d = CAMath::Max(0.5f, 3.5f * CAMath::Sqrt(b.Cov()[1])); if (CAMath::Abs(b.Par()[4]) * Param().qptB5Scaler >= 20) { d *= 2; @@ -824,7 +824,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThrea } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { #if !defined(GPUCA_GPUCODE_COMPILEKERNELS) GPUTPCGMBorderRange* range1 = mBorderRange[iSector1]; @@ -857,7 +857,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<3>(int32_t nBlocks, int32_t nThrea } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { // int32_t statAll = 0, statMerged = 0; float factor2ys = Param().rec.tpc.trackMergerFactor2YS; @@ -884,7 +884,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThrea i2++; } - GPUTPCGMBorderTrack& b1 = B1[r1.fId]; + const GPUTPCGMBorderTrack& b1 = B1[r1.fId]; if (b1.NClusters() < minNPartHits) { continue; } @@ -901,7 +901,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThrea } // do check - GPUTPCGMBorderTrack& b2 = B2[r2.fId]; + const GPUTPCGMBorderTrack& b2 = B2[r2.fId]; #if defined(GPUCA_MERGER_BY_MC_LABEL) && !defined(GPUCA_GPUCODE) int64_t label1 = GetTrackLabel(b1); int64_t label2 = GetTrackLabel(b2); @@ -1016,7 +1016,7 @@ GPUd() void GPUTPCGMMerger::MergeWithinSectorsPrepare(int32_t nBlocks, int32_t n const float maxSin = CAMath::Sin(60.f / 180.f * CAMath::Pi()); for (int32_t itr = iBlock * nThreads + iThread; itr < SectorTrackInfoLocalTotal(); itr += nThreads * nBlocks) { - GPUTPCGMSectorTrack& track = mSectorTrackInfos[itr]; + const GPUTPCGMSectorTrack& track = mSectorTrackInfos[itr]; int32_t iSector = track.Sector(); GPUTPCGMBorderTrack b; if (track.TransportToX(this, x0, Param().bzCLight, b, maxSin)) { @@ -1845,7 +1845,11 @@ GPUd() void GPUTPCGMMerger::PrepareForFit1(int32_t nBlocks, int32_t nThreads, in GPUTPCGMMergedTrack& trk = mMergedTracks[i]; if (trk.OK()) { for (uint32_t j = 0; j < trk.NClusters(); j++) { - mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num] = attachAttached | attachGood; + uint32_t weight = attachAttached | attachGood; + if (CAMath::Abs(trk.GetParam().GetQPt() * Param().qptB5Scaler) <= Param().rec.tpc.rejectQPtB5 && !trk.MergedLooper() && trk.Leg() == 0) { + weight |= attachProtect; + } + CAMath::AtomicMax(&mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num], weight); CAMath::AtomicAdd(&mSharedCount[mClusters[trk.FirstClusterRef() + j].num], 1u); } if (!trk.CCE() && !trk.MergedLooper()) { @@ -1893,7 +1897,9 @@ GPUd() void GPUTPCGMMerger::Finalize0(int32_t nBlocks, int32_t nThreads, int32_t mTrackSort[mTrackOrderAttach[i]] = i; } for (uint32_t i = iBlock * nThreads + iThread; i < mMemory->nMergedTrackClusters; i += nThreads * nBlocks) { - mClusterAttachment[mClusters[i].num] = 0; // Reset adjacent attachment for attached clusters, set correctly below + if (!(mClusterAttachment[mClusters[i].num] & attachProtect)) { + mClusterAttachment[mClusters[i].num] = 0; // Reset adjacent attachment for attached clusters, set correctly below + } } } @@ -1916,6 +1922,9 @@ GPUd() void GPUTPCGMMerger::Finalize1(int32_t nBlocks, int32_t nThreads, int32_t if (trk.Leg() == 0) { weight |= attachGoodLeg; } + if (CAMath::Abs(trk.GetParam().GetQPt() * Param().qptB5Scaler) <= Param().rec.tpc.rejectQPtB5 && !trk.MergedLooper() && trk.Leg() == 0) { + weight |= attachProtect; + } CAMath::AtomicMax(&mClusterAttachment[id], weight); } } @@ -1939,7 +1948,7 @@ GPUd() void GPUTPCGMMerger::MergeLoopersInit(int32_t nBlocks, int32_t nThreads, const float qptabs = CAMath::Abs(p.GetQPt()); if (trk.OK() && trk.NClusters() && trk.Leg() == 0 && qptabs * Param().qptB5Scaler > 5.f && qptabs * Param().qptB5Scaler <= lowPtThresh) { const int32_t sector = mClusters[trk.FirstClusterRef() + trk.NClusters() - 1].sector; - const float refz = p.GetZ() + GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, p.GetTOffset(), Param().continuousMaxTimeBin) + (trk.CSide() ? -100 : 100); + const float refz = p.GetZ() + (Param().par.continuousTracking ? GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, p.GetTOffset(), Param().continuousMaxTimeBin) : 0) + (trk.CSide() ? -100 : 100); float sinA, cosA; CAMath::SinCos(trk.GetAlpha(), sinA, cosA); float gx = cosA * p.GetX() - sinA * p.GetY(); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 14974bdec2303..8f554c24c1d8c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -199,6 +199,8 @@ class GPUTPCGMMerger : public GPUProcessor void DumpRefit(std::ostream& out) const; void DumpFinal(std::ostream& out) const; void DumpLoopers(std::ostream& out) const; + void DumpTrackParam(std::ostream& out) const; + void DumpTrackClusters(std::ostream& out, bool non0StateOnly = false, bool noNDF0 = false) const; template void MergedTrackStreamerInternal(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int32_t sector1, int32_t sector2, int32_t mergeMode, float weight, float frac) const; @@ -224,7 +226,7 @@ class GPUTPCGMMerger : public GPUProcessor private: GPUd() void MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iBorder, GPUTPCGMBorderTrack** B, GPUAtomic(uint32_t) * nB, bool useOrigTrackParam = false); template - GPUd() void MergeBorderTracks(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode = 0); + GPUd() void MergeBorderTracks(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode = 0); GPUd() void MergeCEFill(const GPUTPCGMSectorTrack* track, const GPUTPCGMMergedTrackHit& cls, int32_t itr); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx index 0a83bf47f5725..f6afc46609a11 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx @@ -43,10 +43,10 @@ using namespace gputpcgmmergertypes; void GPUTPCGMMerger::DumpSectorTracks(std::ostream& out) const { std::streamsize ss = out.precision(); - out << std::setprecision(2); + out << std::setprecision(10); out << "\nTPC Merger Sector Tracks\n"; for (int32_t iSector = 0; iSector < NSECTORS; iSector++) { - out << "Sector Track Info Index " << (mSectorTrackInfoIndex[iSector + 1] - mSectorTrackInfoIndex[iSector]) << " / " << (mSectorTrackInfoIndex[NSECTORS + iSector + 1] - mSectorTrackInfoIndex[NSECTORS + iSector]) << "\n"; + out << "Sector Track Info Sector " << iSector << " Index " << (mSectorTrackInfoIndex[iSector + 1] - mSectorTrackInfoIndex[iSector]) << " / " << (mSectorTrackInfoIndex[NSECTORS + iSector + 1] - mSectorTrackInfoIndex[NSECTORS + iSector]) << "\n"; for (int32_t iGlobal = 0; iGlobal < 2; iGlobal++) { out << " Track type " << iGlobal << "\n"; for (int32_t j = mSectorTrackInfoIndex[iSector + NSECTORS * iGlobal]; j < mSectorTrackInfoIndex[iSector + NSECTORS * iGlobal + 1]; j++) { @@ -134,9 +134,14 @@ void GPUTPCGMMerger::DumpMergedBetweenSectors(std::ostream& out) const void GPUTPCGMMerger::DumpCollected(std::ostream& out) const { - std::streamsize ss = out.precision(); - out << std::setprecision(6); out << "\nTPC Merger Collected Tracks\n"; + DumpTrackParam(out); +} + +void GPUTPCGMMerger::DumpTrackParam(std::ostream& out) const +{ + std::streamsize ss = out.precision(); + out << std::setprecision(10); for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { const auto& trk = mMergedTracks[i]; const auto& p = trk.GetParam(); @@ -157,33 +162,42 @@ void GPUTPCGMMerger::DumpMergeCE(std::ostream& out) const } } -void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const +void GPUTPCGMMerger::DumpTrackClusters(std::ostream& out, bool non0StateOnly, bool noNDF0) const { - out << "\nTPC Merger Refit Prepare\n"; - out << " Sort\n"; - for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { - out << " " << i << ": " << mTrackOrderAttach[i] << "\n"; - } - out << " Clusters\n"; for (uint32_t j = 0; j < mMemory->nMergedTracks; j++) { const auto& trk = mMergedTracks[j]; - out << " Track " << j << ": "; + if (trk.NClusters() == 0) { + continue; + } + if (noNDF0 && (!trk.OK() || trk.GetParam().GetNDF() < 0)) { + continue; + } + out << " Track " << j << ": (" << trk.NClusters() << "): "; for (uint32_t i = trk.FirstClusterRef(); i < trk.FirstClusterRef() + trk.NClusters(); i++) { - out << j << "/" << (i - trk.FirstClusterRef()) << ": " << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; + if (!non0StateOnly || mClusters[i].state != 0) { + out << j << "/" << (i - trk.FirstClusterRef()) << ": " << (int32_t)mClusters[i].row << "/" << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; + } } out << "\n"; } - uint32_t maxId = mNMaxClusters; +} + +void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const +{ + out << "\nTPC Merger Refit Prepare\n"; + out << " Sort\n"; + for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { + out << " " << i << ": " << mTrackOrderAttach[i] << "\n"; + } + out << " Track Clusters"; + DumpTrackClusters(out); uint32_t j = 0; - for (uint32_t i = 0; i < maxId; i++) { + for (uint32_t i = 0; i < mNMaxClusters; i++) { if ((mClusterAttachment[i] & attachFlagMask) != 0) { - if (++j % 10 == 0) { - out << " Cluster attachment "; + if (j++ % 10 == 0) { + out << "\n Cluster attachment "; } out << i << ": " << (mClusterAttachment[i] & attachTrackMask) << " / " << (mClusterAttachment[i] & attachFlagMask) << " - "; - if (j % 10 == 0) { - out << "\n"; - } } } out << "\n"; @@ -192,7 +206,7 @@ void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const void GPUTPCGMMerger::DumpRefit(std::ostream& out) const { std::streamsize ss = out.precision(); - out << std::setprecision(2); + out << std::setprecision(10); out << "\nTPC Merger Refit\n"; for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { const auto& trk = mMergedTracks[i]; @@ -224,22 +238,10 @@ void GPUTPCGMMerger::DumpLoopers(std::ostream& out) const void GPUTPCGMMerger::DumpFinal(std::ostream& out) const { out << "\nTPC Merger Finalized\n"; - for (uint32_t j = 0; j < mMemory->nMergedTracks; j++) { - const auto& trk = mMergedTracks[j]; - if (trk.NClusters() == 0) { - continue; - } - out << " Track " << j << ": "; - for (uint32_t i = trk.FirstClusterRef(); i < trk.FirstClusterRef() + trk.NClusters(); i++) { - if (mClusters[i].state != 0) { - out << j << "/" << (i - trk.FirstClusterRef()) << ": " << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; - } - } - out << "\n"; - } - uint32_t maxId = mNMaxClusters; + out << "Track Clusters\n"; + DumpTrackClusters(out, true); uint32_t j = 0; - for (uint32_t i = 0; i < maxId; i++) { + for (uint32_t i = 0; i < mNMaxClusters; i++) { if ((mClusterAttachment[i] & attachFlagMask) != 0) { if (++j % 10 == 0) { out << " Cluster attachment "; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h b/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h index dec72b1d431e6..5d00451516aa8 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h @@ -24,7 +24,7 @@ namespace o2::gpu class GPUTPCGMMergerGeneral : public GPUKernelTemplate { public: - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCMerging; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCMerging; } typedef GPUTPCGMMerger processorType; GPUhdi() static processorType* Processor(GPUConstantMem& processors) { diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h b/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h index ba251ce34a3eb..731439aab0be2 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h @@ -21,7 +21,8 @@ namespace o2::gpu::gputpcgmmergertypes { -enum attachTypes { attachAttached = 0x40000000, +enum attachTypes { attachProtect = 0x80000000, + attachAttached = 0x40000000, attachGood = 0x20000000, attachGoodLeg = 0x10000000, attachTube = 0x08000000, diff --git a/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx b/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx index e911275da1e55..9224904e104c1 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx @@ -33,7 +33,6 @@ using namespace o2::tpc; using namespace o2::tpc::constants; GPUdi() static constexpr uint8_t getFlagsReject() { return GPUTPCGMMergedTrackHit::flagReject | GPUTPCGMMergedTrackHit::flagHighIncl; } -GPUdi() static uint32_t getFlagsRequired(const GPUSettingsRec& rec) { return gputpcgmmergertypes::attachGoodLeg; } namespace o2::gpu::internal { @@ -56,7 +55,6 @@ GPUdii() void GPUTPCGMO2Output::Thread(int32_t nBlock const GPUdEdxInfo* tracksdEdx = merger.MergedTracksdEdx(); constexpr uint8_t flagsReject = getFlagsReject(); - const uint32_t flagsRequired = getFlagsRequired(merger.Param().rec); bool cutOnTrackdEdx = merger.Param().par.dodEdx && merger.Param().dodEdxEnabled && merger.Param().rec.tpc.minTrackdEdxMax2Tot > 0.f; GPUTPCGMMerger::tmpSort* GPUrestrict() trackSort = merger.TrackSortO2(); @@ -71,7 +69,7 @@ GPUdii() void GPUTPCGMO2Output::Thread(int32_t nBlock uint32_t nCl = 0; for (uint32_t j = 0; j < tracks[i].NClusters(); j++) { - if ((trackClusters[tracks[i].FirstClusterRef() + j].state & flagsReject) || (merger.ClusterAttachment()[trackClusters[tracks[i].FirstClusterRef() + j].num] & flagsRequired) != flagsRequired) { + if ((trackClusters[tracks[i].FirstClusterRef() + j].state & flagsReject)) { continue; } nCl++; @@ -115,7 +113,6 @@ GPUdii() void GPUTPCGMO2Output::Thread(int32_t nBlocks const int32_t nTracks = merger.NOutputTracksTPCO2(); const GPUTPCGMMergedTrackHit* trackClusters = merger.Clusters(); constexpr uint8_t flagsReject = getFlagsReject(); - const uint32_t flagsRequired = getFlagsRequired(merger.Param().rec); TrackTPC* outputTracks = merger.OutputTracksTPCO2(); uint32_t* clusRefs = merger.OutputClusRefsTPCO2(); const auto& param = merger.Param(); @@ -191,7 +188,7 @@ GPUdii() void GPUTPCGMO2Output::Thread(int32_t nBlocks int32_t sector1 = 0, sector2 = 0; const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = merger.GetConstantMem()->ioPtrs.clustersNative; for (uint32_t j = 0; j < track.NClusters(); j++) { - if ((trackClusters[track.FirstClusterRef() + j].state & flagsReject) || (merger.ClusterAttachment()[trackClusters[track.FirstClusterRef() + j].num] & flagsRequired) != flagsRequired) { + if ((trackClusters[track.FirstClusterRef() + j].state & flagsReject)) { continue; } int32_t clusterIdGlobal = trackClusters[track.FirstClusterRef() + j].num; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx index bce70ea79f322..6042dec7d4931 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx @@ -37,7 +37,7 @@ GPUd() void GPUTPCGMSectorTrack::Set(const GPUTPCGMMerger* merger, const GPUTPCT mParam.mSecPhi = 1.f / mParam.mCosPhi; mAlpha = alpha; mSector = sector; - mTOffset = merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, t.GetZOffset(), merger->Param().continuousMaxTimeBin); + mTOffset = merger->Param().par.continuousTracking ? merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, t.GetZOffset(), merger->Param().continuousMaxTimeBin) : 0; mNClusters = sectorTr->NHits(); } @@ -323,7 +323,7 @@ GPUd() bool GPUTPCGMSectorTrack::TransportToX(GPUTPCGMMerger* merger, float x, f b.SetPar(2, ey1); b.SetPar(3, param.mDzDs); b.SetPar(4, param.mQPt); - b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin)); + b.SetZOffsetLinear(merger->Param().par.continuousTracking ? merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin) : 0); if (!doCov) { return (1); @@ -478,7 +478,7 @@ GPUd() bool GPUTPCGMSectorTrack::TransportToXAlpha(GPUTPCGMMerger* merger, float b.SetPar(2, ey1); b.SetPar(3, dzds); b.SetPar(4, qpt); - b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin)); + b.SetZOffsetLinear(merger->Param().par.continuousTracking ? merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin) : 0); b.SetCov(0, c00 + h2 * h2c22 + h4 * h4c44 + 2.f * (h2 * c20ph4c42 + h4 * c40)); b.SetCov(1, c11 + dS * (c31 + n7)); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h index 60febbb4428f6..84102cd14ce5c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h @@ -54,6 +54,7 @@ class GPUTPCGMSectorTrack GPUd() float SecPhi() const { return mParam.mSecPhi; } GPUd() float DzDs() const { return mParam.mDzDs; } GPUd() float QPt() const { return mParam.mQPt; } + GPUd() const auto& Param() const { return mParam; } GPUd() float TOffset() const { return mTOffset; } GPUd() int32_t LocalTrackId() const { return mLocalTrackId; } @@ -75,7 +76,7 @@ class GPUTPCGMSectorTrack GPUd() void Set(const GPUTPCGMTrackParam& trk, const GPUTPCTrack* sectorTr, float alpha, int32_t sector); GPUd() void SetParam2(const GPUTPCGMTrackParam& trk); GPUd() void Set(const GPUTPCGMMerger* merger, const GPUTPCTrack* sectorTr, float alpha, int32_t sector); - GPUd() void UseParam2() { mParam = mParam2; } + GPUd() void UseParam2() { mParam = mParam2; } // TODO: Clean this up! GPUd() void SetX2(float v) { mParam2.mX = v; } GPUd() float X2() const { return mParam2.mX; } diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index d865a3b6899b4..70fb9cd1a769e 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -14,11 +14,6 @@ #define GPUCA_CADEBUG 0 #define DEBUG_SINGLE_TRACK -1 -#define EXTRACT_RESIDUALS 0 - -#if EXTRACT_RESIDUALS == 1 -#include "GPUROOTDump.h" -#endif #include "GPUTPCDef.h" #include "GPUTPCGMTrackParam.h" @@ -151,6 +146,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ const float rmax = (83.5f + param.rec.tpc.sysClusErrorMinDist); if (r2 < rmax * rmax) { MarkClusters(clusters, ihitMergeFirst, ihit, wayDirection, GPUTPCGMMergedTrackHit::flagRejectErr); + continue; } } @@ -252,7 +248,6 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ const float invCharge = merger->GetConstantMem()->ioPtrs.clustersNative ? (1.f / merger->GetConstantMem()->ioPtrs.clustersNative->clustersLinear[cluster.num].qMax) : 0.f; float invAvgCharge = (sumInvSqrtCharge += invSqrtCharge) / ++nAvgCharge; invAvgCharge *= invAvgCharge; - prop.GetErr2(err2Y, err2Z, param, zz, cluster.row, clusterState, cluster.sector, time, invAvgCharge, invCharge); if (rejectChi2 >= GPUTPCGMPropagator::rejectInterFill) { @@ -495,7 +490,8 @@ GPUd() float GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestric GPUd() float GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestrict() Merger, int32_t sector, int32_t iRow, int32_t iTrack, bool goodLeg, float Y, float Z) { - if (Merger->Param().rec.tpc.disableRefitAttachment & 1) { + const auto& param = Merger->Param(); + if (param.rec.tpc.disableRefitAttachment & 1) { return -1e6f; } const GPUTPCTracker& GPUrestrict() tracker = *(Merger->GetConstantMem()->tpcTrackers + sector); @@ -506,29 +502,40 @@ GPUd() float GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestric return -1e6f; } - const float zOffset = Merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, mTOffset, Merger->Param().continuousMaxTimeBin); + const float zOffset = param.par.continuousTracking ? Merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, mTOffset, param.continuousMaxTimeBin) : 0; // TODO: do some validatiomns for the transform conv functions... const float y0 = row.Grid().YMin(); const float stepY = row.HstepY(); const float z0 = row.Grid().ZMin() - zOffset; // We can use our own ZOffset, since this is only used temporarily anyway const float stepZ = row.HstepZ(); int32_t bin, ny, nz; + float uncorrectedY, uncorrectedZ; + Merger->GetConstantMem()->calibObjects.fastTransformHelper->InverseTransformYZtoNominalYZ(sector, iRow, Y, Z, uncorrectedY, uncorrectedZ); + if (CAMath::Abs(uncorrectedY) > row.getTPCMaxY()) { + return uncorrectedY; + } + + bool protect = CAMath::Abs(GetQPt() * param.qptB5Scaler) <= param.rec.tpc.rejectQPtB5 && goodLeg; float err2Y, err2Z; - Merger->Param().GetClusterErrors2(sector, iRow, Z, mP[2], mP[3], -1.f, 0.f, 0.f, err2Y, err2Z); // TODO: Use correct time/avgCharge - const float sy2 = CAMath::Min(Merger->Param().rec.tpc.tubeMaxSize2, Merger->Param().rec.tpc.tubeChi2 * (err2Y + CAMath::Abs(mC[0]))); // Cov can be bogus when following circle - const float sz2 = CAMath::Min(Merger->Param().rec.tpc.tubeMaxSize2, Merger->Param().rec.tpc.tubeChi2 * (err2Z + CAMath::Abs(mC[2]))); // In that case we should provide the track error externally + param.GetClusterErrors2(sector, iRow, Z, mP[2], mP[3], -1.f, 0.f, 0.f, err2Y, err2Z); // TODO: Use correct time/avgCharge + const float tubeMaxSize2 = protect ? param.rec.tpc.tubeProtectMaxSize2 : param.rec.tpc.tubeRemoveMaxSize2; + const float tubeMinSize2 = protect ? param.rec.tpc.tubeProtectMinSize2 : 0.f; + float tubeSigma2 = protect ? param.rec.tpc.tubeProtectSigma2 : param.rec.tpc.tubeRemoveSigma2; + uint32_t pad = CAMath::Float2UIntRn(GPUTPCGeometry::LinearY2Pad(sector, iRow, uncorrectedY)); + float time = Merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->InverseTransformInTimeFrame(sector, uncorrectedZ + (param.par.continuousTracking ? Merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, mTOffset, param.continuousMaxTimeBin) : 0), param.continuousMaxTimeBin); // TODO: Simplify this call in TPCFastTransform + if (iRow < param.rec.tpc.tubeExtraProtectMinRow || + pad < param.rec.tpc.tubeExtraProtectEdgePads || pad >= (uint32_t)(GPUTPCGeometry::NPads(iRow) - param.rec.tpc.tubeExtraProtectEdgePads) || + param.GetUnscaledMult(time) / GPUTPCGeometry::Row2X(iRow) > param.rec.tpc.tubeExtraProtectMinOccupancy) { + tubeSigma2 *= protect ? 2 : 0.5; + } + const float sy2 = CAMath::Max(tubeMinSize2, CAMath::Min(tubeMaxSize2, tubeSigma2 * (err2Y + CAMath::Abs(mC[0])))); // Cov can be bogus when following circle + const float sz2 = CAMath::Max(tubeMinSize2, CAMath::Min(tubeMaxSize2, tubeSigma2 * (err2Z + CAMath::Abs(mC[2])))); // In that case we should provide the track error externally const float tubeY = CAMath::Sqrt(sy2); const float tubeZ = CAMath::Sqrt(sz2); const float sy21 = 1.f / sy2; const float sz21 = 1.f / sz2; - float uncorrectedY, uncorrectedZ; - Merger->GetConstantMem()->calibObjects.fastTransformHelper->InverseTransformYZtoNominalYZ(sector, iRow, Y, Z, uncorrectedY, uncorrectedZ); - if (CAMath::Abs(uncorrectedY) > row.getTPCMaxY()) { - return uncorrectedY; - } row.Grid().GetBinArea(uncorrectedY, uncorrectedZ + zOffset, tubeY, tubeZ, bin, ny, nz); - const int32_t nBinsY = row.Grid().Ny(); const int32_t idOffset = tracker.Data().ClusterIdOffset(); const int32_t* ids = &(tracker.Data().ClusterDataIndex()[row.HitNumberOffset()]); @@ -537,6 +544,10 @@ GPUd() float GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestric if (goodLeg) { myWeight |= gputpcgmmergertypes::attachGoodLeg; } + if (protect) { + myWeight |= gputpcgmmergertypes::attachProtect; + } + for (int32_t k = 0; k <= nz; k++) { const int32_t mybin = bin + k * nBinsY; const uint32_t hitFst = firsthit[mybin]; @@ -901,7 +912,7 @@ GPUdii() void GPUTPCGMTrackParam::RefitTrack(GPUTPCGMMergedTrack& GPUrestrict() } // clang-format off - CADEBUG(if (DEBUG_SINGLE_TRACK >= 0 && iTrk != DEBUG_SINGLE_TRACK) { track.SetNClusters(0); track.SetOK(0); return; } ); + CADEBUG(if (DEBUG_SINGLE_TRACK != -1 && iTrk != ((DEBUG_SINGLE_TRACK == -2 && getenv("DEBUG_TRACK")) ? atoi(getenv("DEBUG_TRACK")) : DEBUG_SINGLE_TRACK)) { track.SetNClusters(0); track.SetOK(0); return; } ); // clang-format on int32_t nTrackHits = track.NClusters(); diff --git a/GPU/GPUTracking/Merger/GPUTPCGlobalDebugSortKernels.h b/GPU/GPUTracking/Merger/GPUTPCGlobalDebugSortKernels.h index 726e8cee1f7a7..87204449749b6 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGlobalDebugSortKernels.h +++ b/GPU/GPUTracking/Merger/GPUTPCGlobalDebugSortKernels.h @@ -32,7 +32,7 @@ class GPUTPCGlobalDebugSortKernels : public GPUKernelTemplate mergedTracks1 = 2, mergedTracks2 = 3, borderTracks = 4 }; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCMerging; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCMerging; } typedef GPUTPCGMMerger processorType; GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return &processors.tpcMerger; } diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h index 9b99ffb8402c0..a397e349d29fb 100644 --- a/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h +++ b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h @@ -24,7 +24,7 @@ namespace o2::gpu class GPUTrackingRefitKernel : public GPUKernelTemplate { public: - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCCompression; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCCompression; } enum K : int32_t { mode0asGPU = 0, diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCCreateOccupancyMap.h b/GPU/GPUTracking/SectorTracker/GPUTPCCreateOccupancyMap.h index de8eb8622adb1..2faf0ec668a6f 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCCreateOccupancyMap.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCCreateOccupancyMap.h @@ -29,7 +29,7 @@ class GPUTPCCreateOccupancyMap : public GPUKernelTemplate enum K { defaultKernel = 0, fill = 0, fold = 1 }; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors, Args... args); }; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCCreateTrackingData.h b/GPU/GPUTracking/SectorTracker/GPUTPCCreateTrackingData.h index dc1beacf79d02..8085124653332 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCCreateTrackingData.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCCreateTrackingData.h @@ -32,7 +32,7 @@ class GPUTPCCreateTrackingData : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h b/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h deleted file mode 100644 index 7d9d607b9b88d..0000000000000 --- a/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file GPUTPCDefinitions.h -/// \author Sergey Gorbunov, David Rohr - -#ifndef GPUTPCDEFINITIONS_H -#define GPUTPCDEFINITIONS_H - -#include "AliHLTDataTypes.h" - -namespace GPUTPCDefinitions -{ -extern const AliHLTComponentDataType fgkTrackletsDataType; -} - -#endif // GPUTPCDEFINITIONS_H diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.cxx index eaaefcb278ffe..ee403116cc6da 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.cxx @@ -167,11 +167,11 @@ GPUdii() void GPUTPCExtrapolationTracking::Thread<0>(int32_t nBlocks, int32_t nT return; } const int32_t iSector = tracker.ISector(); - int32_t sectorLeft = (iSector + (GPUDataTypes::NSECTORS / 2 - 1)) % (GPUDataTypes::NSECTORS / 2); - int32_t sectorRight = (iSector + 1) % (GPUDataTypes::NSECTORS / 2); - if (iSector >= (int32_t)GPUDataTypes::NSECTORS / 2) { - sectorLeft += GPUDataTypes::NSECTORS / 2; - sectorRight += GPUDataTypes::NSECTORS / 2; + int32_t sectorLeft = (iSector + (gpudatatypes::NSECTORS / 2 - 1)) % (gpudatatypes::NSECTORS / 2); + int32_t sectorRight = (iSector + 1) % (gpudatatypes::NSECTORS / 2); + if (iSector >= (int32_t)gpudatatypes::NSECTORS / 2) { + sectorLeft += gpudatatypes::NSECTORS / 2; + sectorRight += gpudatatypes::NSECTORS / 2; } PerformExtrapolationTracking(nBlocks, nThreads, iBlock, iThread, tracker.GetConstantMem()->tpcTrackers[sectorLeft], smem, tracker, true); PerformExtrapolationTracking(nBlocks, nThreads, iBlock, iThread, tracker.GetConstantMem()->tpcTrackers[sectorRight], smem, tracker, false); @@ -180,22 +180,22 @@ GPUdii() void GPUTPCExtrapolationTracking::Thread<0>(int32_t nBlocks, int32_t nT GPUd() int32_t GPUTPCExtrapolationTracking::ExtrapolationTrackingSectorOrder(int32_t iSector) { iSector++; - if (iSector == GPUDataTypes::NSECTORS / 2) { + if (iSector == gpudatatypes::NSECTORS / 2) { iSector = 0; } - if (iSector == GPUDataTypes::NSECTORS) { - iSector = GPUDataTypes::NSECTORS / 2; + if (iSector == gpudatatypes::NSECTORS) { + iSector = gpudatatypes::NSECTORS / 2; } return iSector; } GPUd() void GPUTPCExtrapolationTracking::ExtrapolationTrackingSectorLeftRight(uint32_t iSector, uint32_t& left, uint32_t& right) { - left = (iSector + (GPUDataTypes::NSECTORS / 2 - 1)) % (GPUDataTypes::NSECTORS / 2); - right = (iSector + 1) % (GPUDataTypes::NSECTORS / 2); - if (iSector >= (int32_t)GPUDataTypes::NSECTORS / 2) { - left += GPUDataTypes::NSECTORS / 2; - right += GPUDataTypes::NSECTORS / 2; + left = (iSector + (gpudatatypes::NSECTORS / 2 - 1)) % (gpudatatypes::NSECTORS / 2); + right = (iSector + 1) % (gpudatatypes::NSECTORS / 2); + if (iSector >= (int32_t)gpudatatypes::NSECTORS / 2) { + left += gpudatatypes::NSECTORS / 2; + right += gpudatatypes::NSECTORS / 2; } } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.h b/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.h index 91a33d132f136..9e39ba0ce258d 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCExtrapolationTracking.h @@ -30,7 +30,7 @@ class GPUTPCExtrapolationTracking : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; @@ -50,7 +50,7 @@ class GPUTPCExtrapolationTrackingCopyNumbers : public GPUKernelTemplate { public: typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursCleaner.h b/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursCleaner.h index de79b268aea78..2caf09e4886df 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursCleaner.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursCleaner.h @@ -38,7 +38,7 @@ class GPUTPCNeighboursCleaner : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursFinder.h b/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursFinder.h index 6bdc637b6bad6..ea574b5576d37 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursFinder.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCNeighboursFinder.h @@ -48,7 +48,7 @@ class GPUTPCNeighboursFinder : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCSectorDebugSortKernels.h b/GPU/GPUTracking/SectorTracker/GPUTPCSectorDebugSortKernels.h index 520a791b0eb43..8b994be687e72 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCSectorDebugSortKernels.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCSectorDebugSortKernels.h @@ -30,7 +30,7 @@ class GPUTPCSectorDebugSortKernels : public GPUKernelTemplate hitData = 0, startHits = 1, sectorTracks = 2 }; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } typedef GPUTPCTracker processorType; GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsFinder.h b/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsFinder.h index c834b17369f0f..c62eeb315b1f8 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsFinder.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsFinder.h @@ -38,7 +38,7 @@ class GPUTPCStartHitsFinder : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsSorter.h b/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsSorter.h index 0e2fd96dd2690..1cee79d292b84 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsSorter.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCStartHitsSorter.h @@ -38,7 +38,7 @@ class GPUTPCStartHitsSorter : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx index 41530cb629ce8..506f90c55abf3 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx @@ -53,7 +53,7 @@ void GPUTPCTracker::InitializeProcessor() void* GPUTPCTracker::SetPointersDataLinks(void* mem) { return mData.SetPointersLinks(mem); } void* GPUTPCTracker::SetPointersDataWeights(void* mem) { return mData.SetPointersWeights(mem); } -void* GPUTPCTracker::SetPointersDataScratch(void* mem) { return mData.SetPointersScratch(mem, mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging); } +void* GPUTPCTracker::SetPointersDataScratch(void* mem) { return mData.SetPointersScratch(mem, mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCMerging); } void* GPUTPCTracker::SetPointersDataRows(void* mem) { return mData.SetPointersRows(mem); } void* GPUTPCTracker::SetPointersScratch(void* mem) @@ -62,7 +62,7 @@ void* GPUTPCTracker::SetPointersScratch(void* mem) if (mRec->GetProcessingSettings().memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_INDIVIDUAL) { mem = SetPointersTracklets(mem); } - if (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking) { + if (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCSectorTracking) { computePointerWithAlignment(mem, mTrackletTmpStartHits, GPUCA_ROW_COUNT * mNMaxRowStartHits); computePointerWithAlignment(mem, mRowStartHitCountOffset, GPUCA_ROW_COUNT); } @@ -74,7 +74,7 @@ void* GPUTPCTracker::SetPointersScratchHost(void* mem) if (mRec->GetProcessingSettings().keepDisplayMemory) { computePointerWithAlignment(mem, mLinkTmpMemory, mRec->Res(mMemoryResLinks).Size()); } - mem = mData.SetPointersClusterIds(mem, mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging); + mem = mData.SetPointersClusterIds(mem, mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCMerging); return mem; } @@ -84,10 +84,15 @@ void* GPUTPCTracker::SetPointersCommon(void* mem) return mem; } +bool GPUTPCTracker::MemoryReuseAllowed() +{ + return !mRec->GetProcessingSettings().keepDisplayMemory && ((mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCSectorTracking) || mRec->GetProcessingSettings().inKernelParallel == 1 || mRec->GetProcessingSettings().nHostThreads == 1); +} + void GPUTPCTracker::RegisterMemoryAllocation() { AllocateAndInitializeLate(); - bool reuseCondition = !mRec->GetProcessingSettings().keepDisplayMemory && ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking) || mRec->GetProcessingSettings().inKernelParallel == 1 || mRec->GetProcessingSettings().nHostThreads == 1); + bool reuseCondition = MemoryReuseAllowed(); GPUMemoryReuse reLinks{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerDataLinks, (uint16_t)(mISector % mRec->GetProcessingSettings().nStreams)}; mMemoryResLinks = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataLinks, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCSectorLinks", reLinks); mMemoryResSectorScratch = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK | GPUMemoryResource::MEMORY_CUSTOM, "TPCSectorScratch"); @@ -102,9 +107,9 @@ void GPUTPCTracker::RegisterMemoryAllocation() uint32_t type = GPUMemoryResource::MEMORY_SCRATCH; if (mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { // For individual scheme, we allocate tracklets separately, and change the type for the following allocations to custom type |= GPUMemoryResource::MEMORY_CUSTOM; - mMemoryResTracklets = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersTracklets, type, "TPCTrackerTracklets"); + mMemoryResTracklets = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersTracklets, type | GPUMemoryResource::MEMORY_STACK, "TPCTrackerTracklets"); } - mMemoryResOutput = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersOutput, type, "TPCTrackerTracks"); + mMemoryResOutput = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersOutput, type, "TPCTrackerTracks"); // TODO: Ideally this should eventually go on the stack, so that we can free it after the first phase of track merging } GPUhd() void* GPUTPCTracker::SetPointersTracklets(void* mem) @@ -139,8 +144,9 @@ void GPUTPCTracker::SetMaxData(const GPUTrackingInOutPointers& io) } else { mNMaxRowStartHits = mRec->MemoryScalers()->NTPCRowStartHits(mData.NumberOfHits()); } - mNMaxTracklets = mRec->MemoryScalers()->NTPCTracklets(mData.NumberOfHits()); - mNMaxRowHits = mRec->MemoryScalers()->NTPCTrackletHits(mData.NumberOfHits()); + bool lowField = CAMath::Abs(Param().bzkG) < 4; + mNMaxTracklets = mRec->MemoryScalers()->NTPCTracklets(mData.NumberOfHits(), lowField); + mNMaxRowHits = mRec->MemoryScalers()->NTPCTrackletHits(mData.NumberOfHits(), lowField); mNMaxTracks = mRec->MemoryScalers()->NTPCSectorTracks(mData.NumberOfHits()); if (io.clustersNative) { uint32_t sectorOffset = mISector >= GPUCA_NSECTORS / 2 ? GPUCA_NSECTORS / 2 : 0; @@ -153,7 +159,7 @@ void GPUTPCTracker::SetMaxData(const GPUTrackingInOutPointers& io) } mNMaxTrackHits = mRec->MemoryScalers()->NTPCSectorTrackHits(mData.NumberOfHits(), mRec->GetProcessingSettings().tpcInputWithClusterRejection); - if (mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking).par_SORT_STARTHITS) { + if (mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCSectorTracking).par_SORT_STARTHITS) { if (mNMaxStartHits > mNMaxRowStartHits * GPUCA_ROW_COUNT) { mNMaxStartHits = mNMaxRowStartHits * GPUCA_ROW_COUNT; } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h index 2667da4a53977..5efd3ca845410 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h @@ -77,19 +77,19 @@ class GPUTPCTracker : public GPUProcessor GPUdi() static void GetErrors2Seeding(const GPUParam& param, char sector, int32_t iRow, const GPUTPCTrackParam& t, float time, float& ErrY2, float& ErrZ2) { - // param.GetClusterErrors2(sector, iRow, param.GetContinuousTracking() != 0. ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); - param.GetClusterErrorsSeeding2(sector, iRow, param.par.continuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); + // param.GetClusterErrors2(sector, iRow, param.continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); + param.GetClusterErrorsSeeding2(sector, iRow, param.par.continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); } GPUdi() void GetErrors2Seeding(int32_t iRow, const GPUTPCTrackParam& t, float time, float& ErrY2, float& ErrZ2) const { - // Param().GetClusterErrors2(mISector, iRow, Param().GetContinuousTracking() != 0. ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); - Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); + // Param().GetClusterErrors2(mISector, iRow, Param().continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); + Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); } GPUdi() void GetErrors2Seeding(int32_t iRow, float z, float sinPhi, float DzDs, float time, float& ErrY2, float& ErrZ2) const { - // Param().GetClusterErrors2(mISector, iRow, Param().GetContinuousTracking() != 0. ? 125.f : z, sinPhi, DzDs, time, 0.f, 0.f, ErrY2, ErrZ2); - Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking != 0.f ? 125.f : z, sinPhi, DzDs, time, ErrY2, ErrZ2); + // Param().GetClusterErrors2(mISector, iRow, Param().continuousTracking ? 125.f : z, sinPhi, DzDs, time, 0.f, 0.f, ErrY2, ErrZ2); + Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking ? 125.f : z, sinPhi, DzDs, time, ErrY2, ErrZ2); } void SetupCommonMemory(); @@ -103,6 +103,7 @@ class GPUTPCTracker : public GPUProcessor void* SetPointersTracklets(void* mem); void* SetPointersOutput(void* mem); void RegisterMemoryAllocation(); + bool MemoryReuseAllowed(); int16_t MemoryResLinks() const { return mMemoryResLinks; } int16_t MemoryResScratchHost() const { return mMemoryResScratchHost; } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx index 0b22bfa57c89e..567e4e93cc524 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx @@ -131,8 +131,10 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, float x = row.X(); float y = y0 + hh.x * stepY; float z = z0 + hh.y * stepZ; - if (iRow != r.mStartRow || !tracker.Param().par.continuousTracking) { - tParam.ConstrainZ(z, tracker.ISector(), z0, r.mLastZ); + if (iRow != r.mStartRow) { + if (tracker.Param().par.continuousTracking) { + tParam.ConstrainZ(z, tracker.ISector(), z0, r.mLastZ); + } tracker.GetConstantMem()->calibObjects.fastTransformHelper->TransformXYZ(tracker.ISector(), iRow, x, y, z); } if (iRow == r.mStartRow) { @@ -262,7 +264,9 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, rowHit = CALINK_INVAL; break; } - tParam.ConstrainZ(tmpZ, tracker.ISector(), z0, r.mLastZ); + if (tracker.Param().par.continuousTracking) { + tParam.ConstrainZ(tmpZ, tracker.ISector(), z0, r.mLastZ); + } tracker.GetConstantMem()->calibObjects.fastTransformHelper->InverseTransformYZtoX(tracker.ISector(), iRow, tmpY, tmpZ, x); } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.h b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.h index 031c32b2b4334..120797ad4f1eb 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.h @@ -85,7 +85,7 @@ class GPUTPCTrackletConstructor : public GPUKernelTemplate GPUd() static int32_t GPUTPCTrackletConstructorExtrapolationTracking(GPUconstantref() GPUTPCTracker& tracker, GPUsharedref() T& sMem, GPUTPCTrackParam& tParam, int32_t startrow, int32_t increment, int32_t iTracklet, calink* rowHits); typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletSelector.h b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletSelector.h index 070e02fad8222..6a4a7d9013e5a 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletSelector.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletSelector.h @@ -41,7 +41,7 @@ class GPUTPCTrackletSelector : public GPUKernelTemplate }; typedef GPUconstantref() GPUTPCTracker processorType; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCSectorTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TPCSectorTracking; } GPUhdi() static processorType* Processor(GPUConstantMem& processors) { return processors.tpcTrackers; diff --git a/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt b/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt index eeafcfc44142d..9f28fd8cc6fe9 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt +++ b/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt @@ -30,6 +30,8 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") target_link_libraries(${targetName} PUBLIC GPUTracking) endif() +install(DIRECTORY ../tools DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU) +install(DIRECTORY ../../Definitions/Parameters/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/tools FILES_MATCHING REGEX "\\.(python|sh|cmake)") target_compile_definitions(${targetName} PRIVATE $) if(ROOT_FOUND) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 1fa41d55ebbec..a2e74c45fcb86 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -72,6 +72,7 @@ extern GPUSettingsStandalone configStandalone; } GPUReconstruction *rec, *recAsync, *recPipeline; +uint32_t syncAsyncDecodedClusters = 0; GPUChainTracking *chainTracking, *chainTrackingAsync, *chainTrackingPipeline; GPUChainITS *chainITS, *chainITSAsync, *chainITSPipeline; std::string eventsDir; @@ -172,7 +173,7 @@ int32_t ReadConfiguration(int argc, char** argv) return 1; } if (configStandalone.proc.doublePipeline && (configStandalone.runs < 4 || !configStandalone.outputcontrolmem)) { - printf("Double pipeline mode needs at least 3 runs per event and external output. To cycle though multiple events, use --preloadEvents and --runs n for n iterations round-robin\n"); + printf("Double pipeline mode needs at least 4 runs per event and external output. To cycle though multiple events, use --preloadEvents and --runs n for n iterations round-robin\n"); return 1; } if (configStandalone.TF.bunchSim && configStandalone.TF.nMerge) { @@ -196,6 +197,9 @@ int32_t ReadConfiguration(int argc, char** argv) printf("Can only produce QA pdf output when input files are specified!\n"); return 1; } + if (configStandalone.QA.enableLocalOutput && !configStandalone.QA.inputHistogramsOnly && configStandalone.QA.output == "" && configStandalone.QA.plotsDir != "") { + configStandalone.QA.output = configStandalone.QA.plotsDir + "/output.root"; + } if (configStandalone.QA.inputHistogramsOnly) { configStandalone.rundEdx = false; configStandalone.noEvents = true; @@ -214,11 +218,11 @@ int32_t ReadConfiguration(int argc, char** argv) } } if (configStandalone.setO2Settings) { - if (!(configStandalone.inputcontrolmem && configStandalone.outputcontrolmem)) { - printf("setO2Settings requires the usage of --inputMemory and --outputMemory as in O2\n"); - return 1; - } - if (configStandalone.runGPU) { + if (configStandalone.runGPU && configStandalone.proc.debugLevel <= 1) { + if (!(configStandalone.inputcontrolmem && configStandalone.outputcontrolmem)) { + printf("setO2Settings requires the usage of --inputMemory and --outputMemory as in O2\n"); + return 1; + } configStandalone.proc.forceHostMemoryPoolSize = 1024 * 1024 * 1024; } configStandalone.rec.tpc.trackReferenceX = 83; @@ -293,7 +297,8 @@ int32_t SetupReconstruction() printf("Error reading event config file\n"); return 1; } - printf("Read event settings from dir %s (solenoidBz: %f, constBz %d, maxTimeBin %d)\n", eventsDir.c_str(), rec->GetGRPSettings().solenoidBzNominalGPU, (int32_t)rec->GetGRPSettings().constBz, rec->GetGRPSettings().grpContinuousMaxTimeBin); + const char* tmptext = configStandalone.noEvents ? "Using default event settings, no event dir loaded" : "Read event settings from dir "; + printf("%s%s (solenoidBz: %f, constBz %d, maxTimeBin %d)\n", tmptext, configStandalone.noEvents ? "" : eventsDir.c_str(), rec->GetGRPSettings().solenoidBzNominalGPU, (int32_t)rec->GetGRPSettings().constBz, rec->GetGRPSettings().grpContinuousMaxTimeBin); if (configStandalone.testSyncAsync) { recAsync->ReadSettings(eventsDir.c_str()); } @@ -304,6 +309,10 @@ int32_t SetupReconstruction() chainTracking->mConfigQA = &configStandalone.QA; chainTracking->mConfigDisplay = &configStandalone.display; + if (configStandalone.testSyncAsync) { + chainTrackingAsync->mConfigQA = &configStandalone.QA; + chainTrackingAsync->mConfigDisplay = &configStandalone.display; + } GPUSettingsGRP grp = rec->GetGRPSettings(); GPUSettingsRec recSet; @@ -331,9 +340,15 @@ int32_t SetupReconstruction() grp.grpContinuousMaxTimeBin = configStandalone.TF.timeFrameLen * ((double)GPUReconstructionTimeframe::TPCZ / (double)GPUReconstructionTimeframe::DRIFT_TIME) / chainTracking->GetTPCTransformHelper()->getCorrMap()->getVDrift(); } } - if (configStandalone.cont && grp.grpContinuousMaxTimeBin == 0) { + if (configStandalone.setMaxTimeBin != -2) { + grp.grpContinuousMaxTimeBin = configStandalone.setMaxTimeBin; + } else if (configStandalone.cont && grp.grpContinuousMaxTimeBin == 0) { grp.grpContinuousMaxTimeBin = -1; } + if (grp.grpContinuousMaxTimeBin < -1 && !configStandalone.noEvents) { + printf("Invalid maxTimeBin %d\n", grp.grpContinuousMaxTimeBin); + return 1; + } if (rec->GetDeviceType() == GPUReconstruction::DeviceType::CPU) { printf("Standalone Test Framework for CA Tracker - Using CPU\n"); } else { @@ -357,50 +372,59 @@ int32_t SetupReconstruction() procSet.runMC = true; } - steps.steps = GPUDataTypes::RecoStep::AllRecoSteps; + steps.steps = gpudatatypes::RecoStep::AllRecoSteps; if (configStandalone.runTRD != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, configStandalone.runTRD > 0); + steps.steps.setBits(gpudatatypes::RecoStep::TRDTracking, configStandalone.runTRD > 0); } else if (chainTracking->GetTRDGeometry() == nullptr) { - steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, false); - } - if (configStandalone.rundEdx != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, configStandalone.rundEdx > 0); + steps.steps.setBits(gpudatatypes::RecoStep::TRDTracking, false); } if (configStandalone.runCompression != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, configStandalone.runCompression > 0); + steps.steps.setBits(gpudatatypes::RecoStep::TPCCompression, configStandalone.runCompression > 0); } if (configStandalone.runTransformation != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCConversion, configStandalone.runTransformation > 0); + steps.steps.setBits(gpudatatypes::RecoStep::TPCConversion, configStandalone.runTransformation > 0); } - steps.steps.setBits(GPUDataTypes::RecoStep::Refit, configStandalone.runRefit); + steps.steps.setBits(gpudatatypes::RecoStep::Refit, configStandalone.runRefit); if (!configStandalone.runMerger) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCMerging, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); - steps.steps.setBits(GPUDataTypes::RecoStep::Refit, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCMerging, false); + steps.steps.setBits(gpudatatypes::RecoStep::TRDTracking, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCdEdx, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCCompression, false); + steps.steps.setBits(gpudatatypes::RecoStep::Refit, false); } if (configStandalone.TF.bunchSim || configStandalone.TF.nMerge) { - steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, false); + steps.steps.setBits(gpudatatypes::RecoStep::TRDTracking, false); } - steps.inputs.set(GPUDataTypes::InOutType::TPCClusters, GPUDataTypes::InOutType::TRDTracklets); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCDecompression, false); - steps.inputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, false); + steps.inputs.set(gpudatatypes::InOutType::TPCClusters, gpudatatypes::InOutType::TRDTracklets); + steps.steps.setBits(gpudatatypes::RecoStep::TPCDecompression, false); + steps.inputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, false); if (grp.doCompClusterDecode) { - steps.inputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, true); - steps.inputs.setBits(GPUDataTypes::InOutType::TPCClusters, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCDecompression, true); - steps.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, false); + steps.inputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, true); + steps.inputs.setBits(gpudatatypes::InOutType::TPCClusters, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCCompression, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCClusterFinding, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCDecompression, true); + steps.outputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, false); } else if (grp.needsClusterer) { - steps.inputs.setBits(GPUDataTypes::InOutType::TPCRaw, true); - steps.inputs.setBits(GPUDataTypes::InOutType::TPCClusters, false); + steps.inputs.setBits(gpudatatypes::InOutType::TPCRaw, true); + steps.inputs.setBits(gpudatatypes::InOutType::TPCClusters, false); } else { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCClusterFinding, false); + } + + // Set settings for synchronous + GPUChainTracking::ApplySyncSettings(procSet, recSet, steps.steps, configStandalone.testSyncAsync || configStandalone.testSync, configStandalone.rundEdx); + int32_t runAsyncQA = procSet.runQA && !configStandalone.testSyncAsyncQcInSync ? procSet.runQA : 0; + if (configStandalone.testSyncAsync) { + procSet.eventDisplay = nullptr; + if (!configStandalone.testSyncAsyncQcInSync) { + procSet.runQA = false; + } } + // Apply --recoSteps flag last so it takes precedence + // E.g. ApplySyncSettings might enable TPCdEdx, but might not be needed if only clusterizer was requested if (configStandalone.recoSteps >= 0) { steps.steps &= configStandalone.recoSteps; } @@ -409,56 +433,41 @@ int32_t SetupReconstruction() } steps.outputs.clear(); - steps.outputs.setBits(GPUDataTypes::InOutType::TPCMergedTracks, steps.steps.isSet(GPUDataTypes::RecoStep::TPCMerging)); - steps.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, steps.steps.isSet(GPUDataTypes::RecoStep::TPCCompression)); - steps.outputs.setBits(GPUDataTypes::InOutType::TRDTracks, steps.steps.isSet(GPUDataTypes::RecoStep::TRDTracking)); - steps.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, steps.steps.isSet(GPUDataTypes::RecoStep::TPCClusterFinding)); + steps.outputs.setBits(gpudatatypes::InOutType::TPCMergedTracks, steps.steps.isSet(gpudatatypes::RecoStep::TPCMerging)); + steps.outputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, steps.steps.isSet(gpudatatypes::RecoStep::TPCCompression)); + steps.outputs.setBits(gpudatatypes::InOutType::TRDTracks, steps.steps.isSet(gpudatatypes::RecoStep::TRDTracking)); + steps.outputs.setBits(gpudatatypes::InOutType::TPCClusters, steps.steps.isSet(gpudatatypes::RecoStep::TPCClusterFinding)); - if (steps.steps.isSet(GPUDataTypes::RecoStep::TRDTracking)) { + if (steps.steps.isSet(gpudatatypes::RecoStep::TRDTracking)) { if (procSet.createO2Output && !procSet.trdTrackModelO2) { procSet.createO2Output = 1; // Must not be 2, to make sure TPC GPU tracks are still available for TRD } } - if (configStandalone.testSyncAsync || configStandalone.testSync) { - // Set settings for synchronous - if (configStandalone.rundEdx == -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, 0); - } - recSet.useMatLUT = false; - if (configStandalone.testSyncAsync) { - procSet.eventDisplay = nullptr; - } - } - if (configStandalone.proc.rtc.optSpecialCode == -1) { - configStandalone.proc.rtc.optSpecialCode = configStandalone.testSyncAsync || configStandalone.testSync; - } - rec->SetSettings(&grp, &recSet, &procSet, &steps); if (configStandalone.proc.doublePipeline) { recPipeline->SetSettings(&grp, &recSet, &procSet, &steps); } - if (configStandalone.testSyncAsync) { + if (configStandalone.testSyncAsync) { // TODO: Add --async mode / flag // Set settings for asynchronous - steps.steps.setBits(GPUDataTypes::RecoStep::TPCDecompression, true); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, true); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); - steps.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, false); - steps.inputs.setBits(GPUDataTypes::InOutType::TPCRaw, false); - steps.inputs.setBits(GPUDataTypes::InOutType::TPCClusters, false); - steps.inputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, true); - steps.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCDecompression, true); + steps.steps.setBits(gpudatatypes::RecoStep::TPCdEdx, true); + steps.steps.setBits(gpudatatypes::RecoStep::TPCCompression, false); + steps.steps.setBits(gpudatatypes::RecoStep::TPCClusterFinding, false); + steps.inputs.setBits(gpudatatypes::InOutType::TPCRaw, false); + steps.inputs.setBits(gpudatatypes::InOutType::TPCClusters, false); + steps.inputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, true); + steps.outputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, false); procSet.runMC = false; - procSet.runQA = false; + procSet.runQA = runAsyncQA; procSet.eventDisplay = eventDisplay.get(); procSet.runCompressionStatistics = 0; - procSet.rtc.optSpecialCode = 0; if (recSet.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyB) { procSet.tpcInputWithClusterRejection = 1; } recSet.tpc.disableRefitAttachment = 0xFF; recSet.maxTrackQPtB5 = CAMath::Min(recSet.maxTrackQPtB5, recSet.tpc.rejectQPtB5); - recSet.useMatLUT = true; + GPUChainTracking::ApplySyncSettings(procSet, recSet, steps.steps, false, configStandalone.rundEdx); recAsync->SetSettings(&grp, &recSet, &procSet, &steps); } @@ -649,20 +658,15 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU if (tmpRetVal == 0 || tmpRetVal == 2) { OutputStat(chainTrackingUse, iRun == 0 ? nTracksTotal : nullptr, iRun == 0 ? nClustersTotal : nullptr); - if (configStandalone.memoryStat) { - recUse->PrintMemoryStatistics(); - } else if (configStandalone.proc.debugLevel >= 2) { - recUse->PrintMemoryOverview(); - } } if (tmpRetVal == 0 && configStandalone.testSyncAsync) { - if (configStandalone.testSyncAsync) { - printf("Running asynchronous phase\n"); - } vecpod compressedTmpMem(chainTracking->mIOPtrs.tpcCompressedClusters->totalDataSize); memcpy(compressedTmpMem.data(), (const void*)chainTracking->mIOPtrs.tpcCompressedClusters, chainTracking->mIOPtrs.tpcCompressedClusters->totalDataSize); + o2::tpc::CompressedClusters tmp(*chainTracking->mIOPtrs.tpcCompressedClusters); + syncAsyncDecodedClusters = tmp.nAttachedClusters + tmp.nUnattachedClusters; + printf("Running asynchronous phase from %'u compressed clusters\n", syncAsyncDecodedClusters); chainTrackingAsync->mIOPtrs = ioPtrs; chainTrackingAsync->mIOPtrs.tpcCompressedClusters = (o2::tpc::CompressedClustersFlat*)compressedTmpMem.data(); @@ -685,9 +689,6 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU tmpRetVal = recAsync->RunChains(); if (tmpRetVal == 0 || tmpRetVal == 2) { OutputStat(chainTrackingAsync, nullptr, nullptr); - if (configStandalone.memoryStat) { - recAsync->PrintMemoryStatistics(); - } } recAsync->ClearAllocatedMemory(); } @@ -726,7 +727,7 @@ int32_t main(int argc, char** argv) eventsDir = std::string(configStandalone.absoluteEventsDir ? "" : "events/") + configStandalone.eventsDir + "/"; GPUSettingsDeviceBackend deviceSet; - deviceSet.deviceType = configStandalone.runGPU ? GPUDataTypes::GetDeviceType(configStandalone.gpuType.c_str()) : GPUDataTypes::DeviceType::CPU; + deviceSet.deviceType = configStandalone.runGPU ? gpudatatypes::GetDeviceType(configStandalone.gpuType.c_str()) : gpudatatypes::DeviceType::CPU; deviceSet.forceDeviceType = configStandalone.runGPUforce; deviceSet.master = nullptr; recUnique.reset(GPUReconstruction::CreateInstance(deviceSet)); @@ -751,7 +752,6 @@ int32_t main(int argc, char** argv) recAsync->SetDebugLevelTmp(configStandalone.proc.debugLevel); } chainTrackingAsync = recAsync->AddChain(); - chainTrackingAsync->SetQAFromForeignChain(chainTracking); } if (configStandalone.proc.doublePipeline) { if (configStandalone.proc.debugLevel >= 3) { @@ -784,13 +784,17 @@ int32_t main(int argc, char** argv) srand(configStandalone.seed); - for (nEventsInDirectory = 0; true; nEventsInDirectory++) { - std::ifstream in; - in.open((eventsDir + GPUCA_EVDUMP_FILE "." + std::to_string(nEventsInDirectory) + ".dump").c_str(), std::ifstream::binary); - if (in.fail()) { - break; + nEventsInDirectory = 0; + if (!configStandalone.noEvents) { + while (true) { + std::ifstream in; + in.open((eventsDir + GPUCA_EVDUMP_FILE "." + std::to_string(nEventsInDirectory) + ".dump").c_str(), std::ifstream::binary); + if (in.fail()) { + break; + } + in.close(); + nEventsInDirectory++; } - in.close(); } if (configStandalone.TF.bunchSim || configStandalone.TF.nMerge) { @@ -827,11 +831,7 @@ int32_t main(int argc, char** argv) fflush(stdout); for (int32_t i = 0; i < nEvents - configStandalone.StartEvent; i++) { LoadEvent(configStandalone.StartEvent + i, i); - if (configStandalone.proc.debugLevel >= 2) { - printf("Loading event %d\n", i); - } else { - printf(" %d", i); - } + printf(configStandalone.proc.debugLevel >= 2 ? "Loading event %d\n" : " %d", i + configStandalone.StartEvent); fflush(stdout); } printf("\n"); @@ -859,7 +859,7 @@ int32_t main(int argc, char** argv) if (iEvent != configStandalone.StartEvent) { printf("\n"); } - if (configStandalone.noEvents == false && !configStandalone.preloadEvents) { + if (!configStandalone.noEvents && !configStandalone.preloadEvents) { HighResTimer timerLoad; timerLoad.Start(); if (LoadEvent(iEvent, 0)) { @@ -892,12 +892,14 @@ int32_t main(int argc, char** argv) } printf("Loading time: %'d us\n", (int32_t)(1000000 * timerLoad.GetCurrentElapsedTime())); } - printf("Processing Event %d\n", iEvent); nIteration.store(0); nIterationEnd.store(0); double pipelineWalltime = 1.; - if (configStandalone.proc.doublePipeline) { + if (configStandalone.noEvents) { + printf("No processing, no events loaded\n"); + } else if (configStandalone.proc.doublePipeline) { + printf(configStandalone.preloadEvents ? "Processing Events %d to %d in Pipeline\n" : "Processing Event %d in Pipeline %d times\n", iEvent, configStandalone.preloadEvents ? std::min(iEvent + configStandalone.runs - 1, nEvents - 1) : configStandalone.runs); HighResTimer timerPipeline; if (configStandalone.proc.debugLevel < 2 && (RunBenchmark(rec, chainTracking, 1, iEvent, &nTracksTotal, &nClustersTotal) || RunBenchmark(recPipeline, chainTrackingPipeline, 2, iEvent, &nTracksTotal, &nClustersTotal))) { goto breakrun; @@ -910,6 +912,7 @@ int32_t main(int argc, char** argv) pipelineWalltime = timerPipeline.GetElapsedTime() / (configStandalone.runs - 2); printf("Pipeline wall time: %f, %d iterations, %f per event\n", timerPipeline.GetElapsedTime(), configStandalone.runs - 2, pipelineWalltime); } else { + printf("Processing Event %d\n", iEvent); if (RunBenchmark(rec, chainTracking, configStandalone.runs, iEvent, &nTracksTotal, &nClustersTotal)) { goto breakrun; } @@ -934,6 +937,11 @@ int32_t main(int argc, char** argv) printf("%s (Measured %s time - Extrapolated from %d clusters to %d)\n", stat, configStandalone.proc.debugLevel ? "kernel" : "wall", (int32_t)nClusters, (int32_t)nClsPerTF); } } + if (configStandalone.testSyncAsync && chainTracking->mIOPtrs.clustersNative && chainTrackingAsync->mIOPtrs.clustersNative) { + uint32_t rejected = chainTracking->mIOPtrs.clustersNative->nClustersTotal - syncAsyncDecodedClusters; + float rejectionPercentage = (rejected) * 100.f / chainTracking->mIOPtrs.clustersNative->nClustersTotal; + printf("Cluster Rejection: Sync: %'u, Compressed %'u, Async %'u, Rejected %'u (%7.2f%%)\n", chainTracking->mIOPtrs.clustersNative->nClustersTotal, syncAsyncDecodedClusters, chainTrackingAsync->mIOPtrs.clustersNative->nClustersTotal, rejected, rejectionPercentage); + } if (configStandalone.preloadEvents && configStandalone.proc.doublePipeline) { break; @@ -961,6 +969,9 @@ int32_t main(int argc, char** argv) } rec->Finalize(); + if (configStandalone.testSyncAsync) { + recAsync->Finalize(); + } if (configStandalone.outputcontrolmem && rec->IsGPU()) { if (rec->unregisterMemoryForGPU(outputmemory.get()) || (configStandalone.proc.doublePipeline && recPipeline->unregisterMemoryForGPU(outputmemoryPipeline.get()))) { printf("Error unregistering memory\n"); diff --git a/GPU/GPUTracking/Standalone/CMakeLists.txt b/GPU/GPUTracking/Standalone/CMakeLists.txt index 48fbd77c62786..0cf72fd2b4c3e 100644 --- a/GPU/GPUTracking/Standalone/CMakeLists.txt +++ b/GPU/GPUTracking/Standalone/CMakeLists.txt @@ -246,4 +246,3 @@ install(TARGETS ca TPCFastTransformation standalone_support) install(FILES "cmake/makefile" DESTINATION "${CMAKE_INSTALL_PREFIX}") install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${O2_DIR} ${CMAKE_INSTALL_PREFIX}/src)") install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/config.cmake ${CMAKE_INSTALL_PREFIX}/config.cmake)") -install(DIRECTORY tools DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU) diff --git a/GPU/GPUTracking/Standalone/cmake/config.cmake b/GPU/GPUTracking/Standalone/cmake/config.cmake index 88fe418d40e5b..9355311db617c 100644 --- a/GPU/GPUTracking/Standalone/cmake/config.cmake +++ b/GPU/GPUTracking/Standalone/cmake/config.cmake @@ -28,6 +28,7 @@ set(GPUCA_CONFIG_GL3W 0) set(GPUCA_CONFIG_O2 1) set(GPUCA_BUILD_DEBUG 0) set(GPUCA_BUILD_DEBUG_SANITIZE 0) +set(GPUCA_BUILD_DEBUG_HOSTONLY 0) set(GPUCA_DETERMINISTIC_MODE 0) # OFF / NO_FAST_MATH / OPTO2 / GPU / WHOLEO2 #set(GPUCA_CUDA_GCCBIN c++-14) #set(GPUCA_OPENCL_CLANGBIN clang-20) @@ -40,3 +41,4 @@ set(CUDA_COMPUTETARGET "default") # 86 89 #set(GPUCA_CONFIG_COMPILER gcc) # gcc / clang #set(GPUCA_CONFIG_WERROR 1) #add_definitions(-DGPUCA_GPU_DEBUG_PRINT) +#set(GPUCA_OVERRIDE_PARAMETER_FILE "foo.csv") diff --git a/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C b/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C index f6866bb80da05..30d10bcdd2a8e 100644 --- a/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C +++ b/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C @@ -18,7 +18,7 @@ // echo -e '#define GPUCA_GPUTYPE_AMPERE\n#define PARAMETER_FILE "GPUDefParametersDefaults.h"\ngInterpreter->AddIncludePath("'`pwd`'/include/GPU");\n.x share/GPU/tools/dumpGPUDefParam.C("default_AMPERE.par")\n.q\n' | root -l -b #ifndef PARAMETER_FILE -#error Must provide the PARAMETER_FILE as preprocessor define, e.g. -DHEADER_TO_INCLUDE='"GPUDefParametersDefaults.h"' +#error Must provide the PARAMETER_FILE as preprocessor define, e.g. -DPARAMETER_FILE='"GPUDefParametersDefaults.h"' #endif #define GPUCA_GPUCODE diff --git a/GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh b/GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh new file mode 100755 index 0000000000000..0a4f5f5c1656f --- /dev/null +++ b/GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +if [[ -z $3 ]]; then + echo "Usage: dumpGPUParamByArch.sh [JSON or CSV parameter file] [Architecture] [Output File]" + exit 1 +fi + +if ! command -v root &> /dev/null; then + echo "Cannot run root, please make sure ROOT is available and in the parh" + exit 1 +fi + +if [[ ! -f $1 ]]; then + echo "Input file $1 does not exist" + exit 1 +fi + +if [[ -f "include/GPU/GPUDefParametersLoad.inc" ]]; then + LOADDIR=$(realpath "include/GPU") +elif [[ -f "$O2_ROOT/include/GPU/GPUDefParametersLoad.inc" ]]; then + LOADDIR=$(realpath "$O2_ROOT/include/GPU/") +else + echo "Cannot find GPUDefParametersLoad.inc, please run from standalone benchmark folder or set \$O2_ROOT to the standalone or O2 installation" + exit 1 +fi + +set -e + +TMPDIR=$(mktemp -d) +if [[ $? != 0 ]]; then + echo "Failed to create a temporary directory" + exit 1 +fi + +BASE_DIR=$(dirname $(realpath ${BASH_SOURCE[0]})) + +if [[ $1 =~ \.csv$ ]]; then + "${BASE_DIR}"/../../Definitions/Parameters/csv_to_json.sh $1 > "$TMPDIR"/temp.json + JSON_FILE="$TMPDIR"/temp.json +else + JSON_FILE=$(realpath $1) +fi + +cat < "${TMPDIR}"/CMakeLists.txt +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +project(DumpGPUParam NONE) +include($BASE_DIR/../../Definitions/Parameters/gpu_param_header_generator.cmake) +generate_gpu_param_header("${JSON_FILE}" "$2" "${TMPDIR}/GPUDefParametersDefaultsOnTheFly.h" "${TMPDIR}/GPUDefParametersDefaultsDeviceOnTheFly.h") +EOT + +cmake -B "${TMPDIR}" -S"${TMPDIR}" + +echo -e "#define GPUCA_GPUTYPE_$2\n" \ + "#define PARAMETER_FILE \"${TMPDIR}/GPUDefParametersDefaultsOnTheFly.h\"\n" \ + "gInterpreter->AddIncludePath(\"${TMPDIR}\");gInterpreter->AddIncludePath(\"${LOADDIR}\");\n" \ + ".x $BASE_DIR/dumpGPUDefParam.C(\"$3\")\n.q\n" | root -l -b + +echo -e "\nCreated $3 with parameters for $2 architecture from $1" + +rm -Rf "${TMPDIR}" diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h index 2344c089a4436..40dd379eae30a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h @@ -16,7 +16,7 @@ #define O2_GPU_TPCCFCHAINCONTEXT_H #include "clusterFinderDefs.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTPCClusterFinder.h" #include "CfFragment.h" #include diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h index 800ba786c2105..3ca6b52238ed7 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h @@ -47,9 +47,9 @@ class GPUTPCCFChargeMapFiller : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx index ec084c308312e..33ed089890bc4 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx @@ -9,21 +9,16 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUTPCCFCheckPadBaseline.h +/// \file GPUTPCCFCheckPadBaseline.cxx /// \author Felix Weiglhofer #include "GPUTPCCFCheckPadBaseline.h" #include "CfArray2D.h" #include "PackedCharge.h" -#include "GPUTPCGeometry.h" #include "clusterFinderDefs.h" #ifndef GPUCA_GPUCODE -#ifndef GPUCA_NO_VC -#include -#else -#include -#endif +#include "utils/VcShim.h" #endif using namespace o2::gpu; @@ -32,55 +27,91 @@ using namespace o2::gpu::tpccf; template <> GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer) { - const CfFragment& fragment = clusterer.mPmemory->fragment; - CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); - - int32_t basePad = iBlock * PadsPerCacheline; - CfChargePos basePos = padToCfChargePos(basePad, clusterer); +#ifdef GPUCA_GPUCODE + CheckBaselineGPU(nBlocks, nThreads, iBlock, iThread, smem, clusterer); +#else + CheckBaselineCPU(nBlocks, nThreads, iBlock, iThread, smem, clusterer); +#endif +} - if (not basePos.valid()) { +// Charges are stored in a 2D array (pad and time) using a tiling layout. +// Tiles are 8 pads x 4 timebins large stored in time-major layout and make up a single cacheline. +// +// This kernel processes one row per block. Threads cooperatively load chunks +// of 4 consecutive time bins for all pads into shared memory. Thread `i` then processes charges for pad `i` in shared memory. +// Blocks require `nextMultipleOf<64>(138 * 4) = 576` threads to process the largest TPC rows with 138 pads correctly. +GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer) +{ +#ifdef GPUCA_GPUCODE + if (iBlock >= GPUCA_ROW_COUNT) { return; } -#ifdef GPUCA_GPUCODE - static_assert(TPC_MAX_FRAGMENT_LEN_GPU % NumOfCachedTimebins == 0); + const CfFragment& fragment = clusterer.mPmemory->fragment; + CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); + + const auto iRow = iBlock; + const auto rowinfo = GetRowInfo(iRow); + const CfChargePos basePos{(Row)iRow, 0, 0}; int32_t totalCharges = 0; int32_t consecCharges = 0; int32_t maxConsecCharges = 0; Charge maxCharge = 0; - int16_t localPadId = iThread / NumOfCachedTimebins; - int16_t localTimeBin = iThread % NumOfCachedTimebins; - bool handlePad = localTimeBin == 0; + const int16_t iPadOffset = iThread % MaxNPadsPerRow; + const int16_t iTimeOffset = iThread / MaxNPadsPerRow; + const int16_t iPadHandle = iThread; + const bool handlePad = iPadHandle < rowinfo.nPads; + + const auto firstTB = fragment.firstNonOverlapTimeBin(); + const auto lastTB = fragment.lastNonOverlapTimeBin(); + + for (auto t = firstTB; t < lastTB; t += NumOfCachedTBs) { + + const TPCFragmentTime iTime = t + iTimeOffset; + + const CfChargePos pos = basePos.delta({iPadOffset, iTime}); + + smem.charges[iTimeOffset][iPadOffset] = iTime < lastTB && iPadOffset < rowinfo.nPads ? chargeMap[pos].unpack() : 0; - for (tpccf::TPCFragmentTime t = fragment.firstNonOverlapTimeBin(); t < fragment.lastNonOverlapTimeBin(); t += NumOfCachedTimebins) { - const CfChargePos pos = basePos.delta({localPadId, int16_t(t + localTimeBin)}); - smem.charges[localPadId][localTimeBin] = (pos.valid()) ? chargeMap[pos].unpack() : 0; GPUbarrier(); + if (handlePad) { - for (int32_t i = 0; i < NumOfCachedTimebins; i++) { - const Charge q = smem.charges[localPadId][i]; + for (int32_t i = 0; i < NumOfCachedTBs; i++) { + const Charge q = smem.charges[i][iPadHandle]; totalCharges += (q > 0); consecCharges = (q > 0) ? consecCharges + 1 : 0; maxConsecCharges = CAMath::Max(consecCharges, maxConsecCharges); maxCharge = CAMath::Max(q, maxCharge); } } + GPUbarrier(); } - GPUbarrier(); - if (handlePad) { - updatePadBaseline(basePad + localPadId, clusterer, totalCharges, maxConsecCharges, maxCharge); + updatePadBaseline(rowinfo.globalPadOffset + iPadOffset, clusterer, totalCharges, maxConsecCharges, maxCharge); } +#endif +} + +GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineCPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer) +{ +#ifndef GPUCA_GPUCODE + const CfFragment& fragment = clusterer.mPmemory->fragment; + CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); + + int32_t basePad = iBlock * PadsPerCacheline; + int32_t padsPerRow; + CfChargePos basePos = padToCfChargePos(basePad, clusterer, padsPerRow); -#else // CPU CODE + if (not basePos.valid()) { + return; + } constexpr size_t ElemsInTileRow = (size_t)TilingLayout>::WidthInTiles * TimebinsPerCacheline * PadsPerCacheline; -#ifndef GPUCA_NO_VC using UShort8 = Vc::fixed_size_simd; using Charge8 = Vc::fixed_size_simd; @@ -88,12 +119,6 @@ GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThread UShort8 consecCharges{Vc::Zero}; UShort8 maxConsecCharges{Vc::Zero}; Charge8 maxCharge{Vc::Zero}; -#else - std::array totalCharges{0}; - std::array consecCharges{0}; - std::array maxConsecCharges{0}; - std::array maxCharge{0}; -#endif tpccf::TPCFragmentTime t = fragment.firstNonOverlapTimeBin(); @@ -102,7 +127,6 @@ GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThread for (; t < fragment.lastNonOverlapTimeBin(); t += TimebinsPerCacheline) { for (tpccf::TPCFragmentTime localtime = 0; localtime < TimebinsPerCacheline; localtime++) { -#ifndef GPUCA_NO_VC const UShort8 packedCharges{packedChargeStart + PadsPerCacheline * localtime, Vc::Aligned}; const UShort8::mask_type isCharge = packedCharges != 0; @@ -123,22 +147,6 @@ GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThread } else { consecCharges = 0; } -#else // Vc not available - for (tpccf::Pad localpad = 0; localpad < PadsPerCacheline; localpad++) { - const uint16_t packedCharge = packedChargeStart[PadsPerCacheline * localtime + localpad]; - const bool isCharge = packedCharge != 0; - if (isCharge) { - totalCharges[localpad]++; - consecCharges[localpad]++; - maxConsecCharges[localpad] = CAMath::Max(maxConsecCharges[localpad], consecCharges[localpad]); - - const Charge unpackedCharge = Charge(packedCharge) / Charge(1 << PackedCharge::DecimalBits); - maxCharge[localpad] = CAMath::Max(maxCharge[localpad], unpackedCharge); - } else { - consecCharges[localpad] = 0; - } - } -#endif } packedChargeStart += ElemsInTileRow; @@ -150,7 +158,8 @@ GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThread #endif } -GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder& clusterer) +template +GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder& clusterer, int32_t& padsPerRow) { constexpr GPUTPCGeometry geo; @@ -158,17 +167,31 @@ GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, cons for (Row r = 0; r < GPUCA_ROW_COUNT; r++) { int32_t npads = geo.NPads(r); int32_t padInRow = pad - padOffset; - if (0 <= padInRow && padInRow < CAMath::nextMultipleOf(npads)) { - int32_t cachelineOffset = padInRow % PadsPerCacheline; + if (0 <= padInRow && padInRow < npads) { + int32_t cachelineOffset = padInRow % PadsPerBlock; pad -= cachelineOffset; + padsPerRow = npads; return CfChargePos{r, Pad(padInRow - cachelineOffset), 0}; } padOffset += npads; } + padsPerRow = 0; return CfChargePos{0, 0, INVALID_TIME_BIN}; } +GPUd() GPUTPCCFCheckPadBaseline::RowInfo GPUTPCCFCheckPadBaseline::GetRowInfo(int16_t row) +{ + constexpr GPUTPCGeometry geo; + + int16_t padOffset = 0; + for (int16_t r = 0; r < row; r++) { + padOffset += geo.NPads(r); + } + + return RowInfo{padOffset, geo.NPads(row)}; +} + GPUd() void GPUTPCCFCheckPadBaseline::updatePadBaseline(int32_t pad, const GPUTPCClusterFinder& clusterer, int32_t totalCharges, int32_t consecCharges, Charge maxCharge) { const CfFragment& fragment = clusterer.mPmemory->fragment; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h index 2403aa6d29ecd..a71f1358a73a6 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h @@ -11,14 +11,20 @@ /// \file GPUTPCCFCheckPadBaseline.h /// \author Felix Weiglhofer +/// +/// Kernel identifies noisy TPC pads by analyzing charge patterns over time. +/// A pad is marked noisy if it exceeds thresholds for total or consecutive +/// time bins with charge, unless the charge exceeds a saturation threshold. #ifndef O2_GPU_GPU_TPC_CF_CHECK_PAD_BASELINE_H #define O2_GPU_GPU_TPC_CF_CHECK_PAD_BASELINE_H #include "GPUGeneralKernels.h" #include "GPUConstantMem.h" +#include "GPUTPCGeometry.h" #include "clusterFinderDefs.h" +#include "CfArray2D.h" namespace o2::gpu { @@ -28,13 +34,20 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate public: enum { - PadsPerCacheline = 8, - TimebinsPerCacheline = 4, - NumOfCachedTimebins = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFCheckPadBaseline) / PadsPerCacheline, + PadsPerCacheline = TPCMapMemoryLayout::Width, + TimebinsPerCacheline = TPCMapMemoryLayout::Height, + EntriesPerCacheline = PadsPerCacheline * TimebinsPerCacheline, + NumOfCachedPads = GPUCA_WARP_SIZE / TimebinsPerCacheline, + NumCLsPerWarp = GPUCA_WARP_SIZE / EntriesPerCacheline, + NumOfCachedTBs = TimebinsPerCacheline, + // Threads index shared memory as [iThread / MaxNPadsPerRow][iThread % MaxNPadsPerRow]. + // Rounding up to a multiple of PadsPerCacheline ensures iThread / MaxNPadsPerRow < NumOfCachedTBs + // for all threads, avoiding out-of-bounds access. + MaxNPadsPerRow = CAMath::nextMultipleOf(GPUTPCGeometry::MaxNPadsPerRow()), }; struct GPUSharedMemory { - tpccf::Charge charges[PadsPerCacheline][NumOfCachedTimebins]; + tpccf::Charge charges[NumOfCachedTBs][MaxNPadsPerRow]; }; typedef GPUTPCClusterFinder processorType; @@ -43,16 +56,33 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; + } + + static int32_t GetNBlocks(bool isGPU) + { + const int32_t nBlocks = TPC_PADS_IN_SECTOR / PadsPerCacheline; + return isGPU ? GPUCA_ROW_COUNT : nBlocks; } template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); private: - GPUd() static CfChargePos padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder&); + GPUd() static void CheckBaselineGPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); + GPUd() static void CheckBaselineCPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); + + template + GPUd() static CfChargePos padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder&, int32_t& padsPerRow); + + struct RowInfo { + int16_t globalPadOffset; + int16_t nPads; + }; + GPUd() static RowInfo GetRowInfo(int16_t row); + GPUd() static void updatePadBaseline(int32_t pad, const GPUTPCClusterFinder&, int32_t totalCharges, int32_t consecCharges, tpccf::Charge maxCharge); }; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h index 70e21db81756c..09814b464651c 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h @@ -49,9 +49,9 @@ class GPUTPCCFClusterizer : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index f7bb64106fe4f..e20f5d8b0f074 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -23,6 +23,7 @@ #include "TPCPadGainCalib.h" #include "TPCZSLinkMapping.h" #include "GPUTPCGeometry.h" +#include "DetectorsRaw/RDHUtils.h" using namespace o2::gpu; using namespace o2::gpu::tpccf; @@ -36,12 +37,12 @@ using namespace o2::tpc::constants; // =========================================================================== template <> -GPUdii() void GPUTPCCFDecodeZS::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF) +GPUdii() void GPUTPCCFDecodeZS::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut) { - GPUTPCCFDecodeZS::decode(clusterer, smem, nBlocks, nThreads, iBlock, iThread, firstHBF); + GPUTPCCFDecodeZS::decode(clusterer, smem, nBlocks, nThreads, iBlock, iThread, firstHBF, tpcTimeBinCut); } -GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUSharedMemory& s, int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t firstHBF) +GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUSharedMemory& s, int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t firstHBF, int32_t tpcTimeBinCut) { const uint32_t sector = clusterer.mISector; #ifdef GPUCA_GPUCODE @@ -173,12 +174,14 @@ GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUShared } const CfFragment& fragment = clusterer.mPmemory->fragment; TPCTime globalTime = timeBin + l; - bool inFragment = fragment.contains(globalTime); + bool discardTimeBin = not fragment.contains(globalTime); + discardTimeBin |= (tpcTimeBinCut > 0 && globalTime > tpcTimeBinCut); + Row row = rowOffset + m; - CfChargePos pos(row, Pad(pad), inFragment ? fragment.toLocal(globalTime) : INVALID_TIME_BIN); + CfChargePos pos(row, Pad(pad), discardTimeBin ? INVALID_TIME_BIN : fragment.toLocal(globalTime)); positions[nDigitsTmp++] = pos; - if (inFragment) { + if (!discardTimeBin) { float q = float(byte & mask) * decodeBitsFactor; q *= clusterer.GetConstantMem()->calibObjects.tpcPadGain->getGainCorrection(sector, row, pad); chargeMap[pos] = PackedCharge(q); @@ -209,71 +212,58 @@ GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUShared // =========================================================================== template <> -GPUdii() void GPUTPCCFDecodeZSLink::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF) +GPUdii() void GPUTPCCFDecodeZSLink::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut) { - Decode(nBlocks, nThreads, iBlock, iThread, smem, clusterer, firstHBF); + Decode(nBlocks, nThreads, iBlock, iThread, smem, clusterer, firstHBF, tpcTimeBinCut); } -GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, processorType& clusterer, int32_t iBlock, int32_t nThreads, int32_t iThread, const uint8_t* page, uint32_t pageDigitOffset, int32_t firstHBF) +GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& ctx) { - const CfFragment& fragment = clusterer.mPmemory->fragment; + const CfFragment& fragment = ctx.clusterer.mPmemory->fragment; - const auto* rdHdr = ConsumeHeader(page); + const auto* rdHdr = ConsumeHeader(ctx.page); if (o2::raw::RDHUtils::getMemorySize(*rdHdr) == sizeof(o2::header::RAWDataHeader)) { - return pageDigitOffset; + return ctx.pageDigitOffset; } [[maybe_unused]] int32_t nDecoded = 0; - const auto* decHdr = ConsumeHeader(page); - ConsumeBytes(page, decHdr->firstZSDataOffset * 16); + const auto* decHdr = ConsumeHeader(ctx.page); + ConsumeBytes(ctx.page, decHdr->firstZSDataOffset * 16); assert(decHdr->version == ZSVersionLinkBasedWithMeta); assert(decHdr->magicWord == o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader); for (uint32_t t = 0; t < decHdr->nTimebinHeaders; t++) { - const auto* tbHdr = ConsumeHeader(page); - const auto* adcData = ConsumeBytes(page, tbHdr->numWordsPayload * 16); // Page now points to next timebin or past the page + const auto* tbHdr = ConsumeHeader(ctx.page); + const auto* adcData = ConsumeBytes(ctx.page, tbHdr->numWordsPayload * 16); // Page now points to next timebin or past the page - int32_t timeBin = (decHdr->timeOffset + tbHdr->bunchCrossing + (uint64_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdHdr) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; + int32_t timeBin = (decHdr->timeOffset + tbHdr->bunchCrossing + (uint64_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdHdr) - ctx.firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; uint32_t channelMask[3]; GetChannelBitmask(*tbHdr, channelMask); uint32_t nAdc = CAMath::Popcount(channelMask[0]) + CAMath::Popcount(channelMask[1]) + CAMath::Popcount(channelMask[2]); - bool inFragment = fragment.contains(timeBin); nDecoded += nAdc; - // TimeBin not in fragment: Skip this timebin header and fill positions with dummy values instead - if (not inFragment) { - pageDigitOffset += FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nAdc); - continue; + bool discardTimeBin = not fragment.contains(timeBin); + discardTimeBin |= (ctx.tpcTimeBinCut > 0 && timeBin > ctx.tpcTimeBinCut); + + if (discardTimeBin) { + FillWithInvalid(ctx.clusterer, ctx.iThread, ctx.nThreads, ctx.pageDigitOffset, nAdc); + } else { + DecodeTB( + smem, + ctx, + adcData, + nAdc, + channelMask, + timeBin, + decHdr->cruID, + tbHdr->fecInPartition); } -#ifdef GPUCA_GPUCODE - DecodeTBMultiThread( - clusterer, - iThread, - smem, - adcData, - nAdc, - channelMask, - timeBin, - decHdr->cruID, - tbHdr->fecInPartition, - pageDigitOffset); -#else // CPU - DecodeTBSingleThread( - clusterer, - adcData, - nAdc, - channelMask, - timeBin, - decHdr->cruID, - tbHdr->fecInPartition, - pageDigitOffset); -#endif - pageDigitOffset += nAdc; + ctx.pageDigitOffset += nAdc; } // for (uint32_t t = 0; t < decHdr->nTimebinHeaders; t++) #ifdef GPUCA_CHECK_TPCZS_CORRUPTION @@ -286,82 +276,25 @@ GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, processorT #endif*/ } #endif - return pageDigitOffset; -} - -GPUd() void GPUTPCCFDecodeZSLink::DecodeTBSingleThread( - processorType& clusterer, - const uint8_t* adcData, - uint32_t nAdc, - const uint32_t* channelMask, - int32_t timeBin, - int32_t cru, - int32_t fecInPartition, - uint32_t pageDigitOffset) -{ - const CfFragment& fragment = clusterer.mPmemory->fragment; - - if constexpr (TPCZSHDRV2::TIGHTLY_PACKED_V3) { - - uint32_t byte = 0, bits = 0, nSamplesWritten = 0, rawFECChannel = 0; - // unpack adc values, assume tightly packed data - while (nSamplesWritten < nAdc) { - byte |= adcData[0] << bits; - adcData++; - bits += CHAR_BIT; - while (bits >= DECODE_BITS) { - - // Find next channel with data - for (; !ChannelIsActive(channelMask, rawFECChannel); rawFECChannel++) { - } - - // Unpack data for cluster finder - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannel, fecInPartition); - - WriteCharge(clusterer, byte, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + nSamplesWritten); - - byte = byte >> DECODE_BITS; - bits -= DECODE_BITS; - nSamplesWritten++; - rawFECChannel++; // Ensure we don't decode same channel twice - } // while (bits >= DECODE_BITS) - } // while (nSamplesWritten < nAdc) - - } else { // ! TPCZSHDRV2::TIGHTLY_PACKED_V3 - uint32_t rawFECChannel = 0; - const uint64_t* adcData64 = (const uint64_t*)adcData; - for (uint32_t j = 0; j < nAdc; j++) { - for (; !ChannelIsActive(channelMask, rawFECChannel); rawFECChannel++) { - } - - uint32_t adc = (adcData64[j / TPCZSHDRV2::SAMPLESPER64BIT] >> ((j % TPCZSHDRV2::SAMPLESPER64BIT) * DECODE_BITS)) & DECODE_MASK; - - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannel, fecInPartition); - float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(clusterer, charge, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + j); - rawFECChannel++; - } - } + return ctx.pageDigitOffset; } -GPUd() void GPUTPCCFDecodeZSLink::DecodeTBMultiThread( - processorType& clusterer, - int32_t iThread, +GPUd() void GPUTPCCFDecodeZSLink::DecodeTB( GPUSharedMemory& smem, + DecodeCtx& ctx, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, - int32_t fecInPartition, - uint32_t pageDigitOffset) + int32_t fecInPartition) { constexpr int32_t NTHREADS = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFDecodeZSLink); static_assert(NTHREADS == GPUCA_WARP_SIZE, "Decoding TB Headers in parallel assumes block size is a single warp."); uint8_t blockOffset = 0; - for (uint8_t i = iThread; blockOffset < nAdc; i += NTHREADS) { + for (uint8_t i = ctx.iThread; blockOffset < nAdc; i += NTHREADS) { uint8_t rawFECChannel = i; @@ -370,26 +303,6 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTBMultiThread( uint8_t myOffset = warp_scan_inclusive_add(myChannelActive) - 1 + blockOffset; blockOffset = warp_broadcast(myOffset, NTHREADS - 1) + 1; - // Decode entire timebin at once if we have enough threads - // This should further improve performance, but code below is buggy... - // if (nAdc <= NThreads) { - // for (int32_t j = 1; blockOffset < nAdc; j++) { - // rawFECChannel = myChannelActive ? rawFECChannel : (iThread + j*NThreads - myOffset); - - // bool iAmIdle = not myChannelActive; - - // myChannelActive = - // rawFECChannel < zerosupp_link_based::CommonHeaderlPerTBHeader - // ? BitIsSet(channelMask, rawFECChannel) - // : false; - - // uint8_t newOffset = warp_scan_inclusive_add(static_cast(myChannelActive && iAmIdle)) - 1 + blockOffset; - // blockOffset = warp_broadcast(newOffset, NThreads - 1) + 1; - - // myOffset = iAmIdle ? newOffset : myOffset; - // } - // } - if (not myChannelActive) { continue; } @@ -399,28 +312,16 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTBMultiThread( if constexpr (TPCZSHDRV2::TIGHTLY_PACKED_V3) { - // Try to access adcData with 4 byte reads instead of 1 byte. - // You'd think this would improve performace, but it's actually slower... - // const uint32_t* adcDataU32 = reinterpret_cast(adcData); - uint32_t adcBitOffset = myOffset * DECODE_BITS; uint32_t adcByteOffset = adcBitOffset / CHAR_BIT; uint32_t adcOffsetInByte = adcBitOffset - adcByteOffset * CHAR_BIT; - // uint32_t adcByteOffset = adcBitOffset / 32; - // uint32_t adcOffsetInByte = adcBitOffset - adcByteOffset * 32; uint32_t byte = 0, bits = 0; - // uint32_t byte = adcDataU32[adcByteOffset] >> adcOffsetInByte; - // uint32_t bits = 32 - adcOffsetInByte; - // adcByteOffset++; - while (bits < DECODE_BITS) { byte |= ((uint32_t)adcData[adcByteOffset]) << bits; - // byte |= adcDataU32[adcByteOffset] << bits; adcByteOffset++; bits += CHAR_BIT; - // bits += 32; } adc = byte >> adcOffsetInByte; @@ -429,10 +330,10 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTBMultiThread( adc = (adcData64[myOffset / TPCZSHDRV2::SAMPLESPER64BIT] >> ((myOffset % TPCZSHDRV2::SAMPLESPER64BIT) * DECODE_BITS)) & DECODE_MASK; } - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannel, fecInPartition); - const CfFragment& fragment = clusterer.mPmemory->fragment; + o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannel, fecInPartition); + const CfFragment& fragment = ctx.clusterer.mPmemory->fragment; float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(clusterer, charge, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + myOffset); + WriteCharge(ctx.clusterer, charge, padAndRow, fragment.toLocal(timeBin), ctx.pageDigitOffset + myOffset); } // for (uint8_t i = iThread; blockOffset < nAdc; i += NThreads) } @@ -462,7 +363,7 @@ GPUd() bool GPUTPCCFDecodeZSLink::ChannelIsActive(const uint32_t* chan, uint8_t // =========================================================================== template -GPUd() void GPUTPCCFDecodeZSLinkBase::Decode(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, typename Decoder::GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF) +GPUd() void GPUTPCCFDecodeZSLinkBase::Decode(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, typename Decoder::GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut) { const uint32_t sector = clusterer.mISector; @@ -507,7 +408,18 @@ GPUd() void GPUTPCCFDecodeZSLinkBase::Decode(int32_t nBlocks, int32_t nThreads, #endif } - pageDigitOffset = Decoder::DecodePage(smem, clusterer, iBlock, nThreads, iThread, page, pageDigitOffset, firstHBF); + DecodeCtx ctx{ + .clusterer = clusterer, + .page = page, + .iBlock = iBlock, + .nThreads = nThreads, + .iThread = iThread, + .pageDigitOffset = pageDigitOffset, + .firstHBF = firstHBF, + .tpcTimeBinCut = tpcTimeBinCut, + }; + + pageDigitOffset = Decoder::DecodePage(smem, ctx); } // [CPU] for (uint32_t j = minJ; j < maxJ; j++) } // [CPU] for (uint32_t i = clusterer.mMinMaxCN[endpoint].zsPtrFirst; i < clusterer.mMinMaxCN[endpoint].zsPtrLast; i++) @@ -585,27 +497,18 @@ GPUd() uint16_t GPUTPCCFDecodeZSLinkBase::FillWithInvalid(processorType& cluster // =========================================================================== template <> -GPUd() void GPUTPCCFDecodeZSDenseLink::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF) +GPUd() void GPUTPCCFDecodeZSDenseLink::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut) { - Decode(nBlocks, nThreads, iBlock, iThread, smem, clusterer, firstHBF); + Decode(nBlocks, nThreads, iBlock, iThread, smem, clusterer, firstHBF, tpcTimeBinCut); } -GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, processorType& clusterer, int32_t iBlock, int32_t nThreads, int32_t iThread, const uint8_t* page, uint32_t pageDigitOffset, int32_t firstHBF) +GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& ctx) { -#ifdef GPUCA_GPUCODE - constexpr bool DecodeInParallel = true; -#else - constexpr bool DecodeInParallel = false; -#endif - - const uint8_t* const pageStart = page; + const uint8_t* const pageStart = ctx.page; - const auto* rawDataHeader = Peek(page); - const auto* decHeader = Peek(page, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2)); - ConsumeHeader(page); - - assert(decHeader->version >= ZSVersionDenseLinkBased); - assert(decHeader->magicWord == tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader); + const auto* rawDataHeader = Peek(ctx.page); + const auto* decHeader = Peek(ctx.page, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2)); + ConsumeHeader(ctx.page); uint16_t nSamplesWritten = 0; const uint16_t nSamplesInPage = decHeader->nADCsamples; @@ -613,87 +516,108 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro const auto* payloadEnd = Peek(pageStart, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2) - ((decHeader->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0)); const auto* nextPage = Peek(pageStart, TPCZSHDR::TPC_ZS_PAGE_SIZE); - ConsumeBytes(page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); + const bool extendsToNextPage = decHeader->flags & TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage; + + ConsumeBytes(ctx.page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); - for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) { + int err = GPUErrors::ERROR_NONE; - [[maybe_unused]] ptrdiff_t sizeLeftInPage = payloadEnd - page; - assert(sizeLeftInPage > 0); + if (decHeader->version < ZSVersionDenseLinkBased) { + err = GPUErrors::ERROR_TPCZS_VERSION_MISMATCH; + } - uint16_t nSamplesWrittenTB = 0; + if (decHeader->magicWord != zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) { + err = GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD; + } + + for (uint16_t i = 0; i < decHeader->nTimebinHeaders && !err; i++) { + + ptrdiff_t sizeLeftInPage = payloadEnd - ctx.page; + if (sizeLeftInPage <= 0) { + err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; + break; + } + + int16_t nSamplesWrittenTB = 0; + uint16_t nSamplesLeftInPage = nSamplesInPage - nSamplesWritten; + + if (i == decHeader->nTimebinHeaders - 1 && extendsToNextPage) { + if (raw::RDHUtils::getMemorySize(*rawDataHeader) != TPCZSHDR::TPC_ZS_PAGE_SIZE) { + err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; + break; + } - if (i == decHeader->nTimebinHeaders - 1 && decHeader->flags & o2::tpc::TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage) { - assert(o2::raw::RDHUtils::getMemorySize(*rawDataHeader) == TPCZSHDR::TPC_ZS_PAGE_SIZE); if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } else { - nSamplesWrittenTB = FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); -#ifdef GPUCA_CHECK_TPCZS_CORRUPTION - if (iThread == 0) { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); - } -#endif + err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF; + break; } } else { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + } + + // Abort decoding the page if an error was detected. + if (nSamplesWrittenTB < 0) { + err = -nSamplesWrittenTB; + break; } - assert(nSamplesWritten <= nSamplesInPage); nSamplesWritten += nSamplesWrittenTB; - pageDigitOffset += nSamplesWrittenTB; + ctx.pageDigitOffset += nSamplesWrittenTB; } // for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) -#ifdef GPUCA_CHECK_TPCZS_CORRUPTION - if (iThread == 0 && nSamplesWritten != nSamplesInPage) { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_NADC, clusterer.mISector * 1000 + decHeader->cruID, nSamplesInPage, nSamplesWritten); - /*#ifndef GPUCA_GPUCODE - FILE* foo = fopen("dump.bin", "w+b"); - fwrite(pageSrc, 1, o2::raw::RDHUtils::getMemorySize(*rdHdr), foo); - fclose(foo); - #endif*/ + if (nSamplesWritten != nSamplesInPage) { + if (nSamplesWritten < nSamplesInPage) { + ctx.pageDigitOffset += FillWithInvalid(ctx.clusterer, ctx.iThread, ctx.nThreads, ctx.pageDigitOffset, nSamplesInPage - nSamplesWritten); + } + err = !err ? GPUErrors::ERROR_TPCZS_INVALID_NADC : err; // Ensure we don't overwrite any previous error } -#endif - - return pageDigitOffset; -} -template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( - processorType& clusterer, - [[maybe_unused]] GPUSharedMemory& smem, - int32_t iThread, - const uint8_t*& page, - uint32_t pageDigitOffset, - const header::RAWDataHeader* rawDataHeader, - int32_t firstHBF, - int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) -{ + if (ctx.iThread == 0 && err) { + [[maybe_unused]] bool dumpPage = false; + + if (err == GPUErrors::ERROR_TPCZS_VERSION_MISMATCH) { + ctx.clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased); + } else if (err == GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD) { + ctx.clusterer.raiseError(err, decHeader->magicWord); + } else if (err == GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF) { + ctx.clusterer.raiseError(err, ctx.clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); + } else if (err == GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW) { + ctx.clusterer.raiseError(err, extendsToNextPage); + dumpPage = true; + } else if (err == GPUErrors::ERROR_TPCZS_INVALID_NADC) { + ctx.clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage); + dumpPage = true; + } else { + ctx.clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err); + } - if constexpr (DecodeInParallel) { - return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); - } else { - uint16_t nSamplesWritten = 0; - if (iThread == 0) { - nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); +#ifdef GPUCA_CHECK_TPCZS_CORRUPTION +#ifndef GPUCA_GPUCODE + if (dumpPage) { + // allocate more space on the stack for fname, so it can be overwritten by hand in a debugger. + const char fname[64] = "dump00.bin"; + FILE* foo = fopen(fname, "w+b"); + fwrite(pageStart, 1, TPCZSHDR::TPC_ZS_PAGE_SIZE, foo); + fclose(foo); } - return warp_broadcast(nSamplesWritten, 0); +#endif +#endif } + + return ctx.pageDigitOffset; } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( - processorType& clusterer, +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( GPUSharedMemory& smem, - const int32_t iThread, - const uint8_t*& page, - uint32_t pageDigitOffset, + DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, - int32_t firstHBF, int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) + uint16_t nSamplesLeftInPage, + const uint8_t* payloadEnd, + const uint8_t* nextPage) { #define MAYBE_PAGE_OVERFLOW(pagePtr) \ if constexpr (PayloadExtendsToNextPage) { \ @@ -703,7 +627,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - assert(pagePtr <= payloadEnd); \ + if (pagePtr > payloadEnd) { \ + return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ + } \ } #define PEEK_OVERFLOW(pagePtr, offset) \ @@ -716,47 +642,45 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( constexpr int32_t NTHREADS = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFDecodeZSDenseLink); static_assert(NTHREADS == GPUCA_WARP_SIZE, "Decoding TB Headers in parallel assumes block size is a single warp."); - const CfFragment& fragment = clusterer.mPmemory->fragment; + const CfFragment& fragment = ctx.clusterer.mPmemory->fragment; // Read timebin block header - uint16_t tbbHdr = ConsumeByte(page); - MAYBE_PAGE_OVERFLOW(page); - tbbHdr |= static_cast(ConsumeByte(page)) << CHAR_BIT; - MAYBE_PAGE_OVERFLOW(page); + uint16_t tbbHdr = ConsumeByte(ctx.page); + MAYBE_PAGE_OVERFLOW(ctx.page); + tbbHdr |= static_cast(ConsumeByte(ctx.page)) << CHAR_BIT; + MAYBE_PAGE_OVERFLOW(ctx.page); uint8_t nLinksInTimebin = tbbHdr & 0x000F; uint16_t linkBC = (tbbHdr & 0xFFF0) >> 4; - int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - firstHBF) * constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; + int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - ctx.firstHBF) * constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; - uint16_t nSamplesInTB = 0; + int16_t nSamplesInTB = 0; // Read timebin link headers for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) { - uint8_t timebinLinkHeaderStart = ConsumeByte(page); - MAYBE_PAGE_OVERFLOW(page); + uint8_t timebinLinkHeaderStart = ConsumeByte(ctx.page); + MAYBE_PAGE_OVERFLOW(ctx.page); - if (iThread == 0) { + if (ctx.iThread == 0) { smem.linkIds[iLink] = timebinLinkHeaderStart & 0b00011111; } bool bitmaskIsFlat = timebinLinkHeaderStart & 0b00100000; uint16_t bitmaskL2 = 0x03FF; if (not bitmaskIsFlat) { - bitmaskL2 = static_cast(timebinLinkHeaderStart & 0b11000000) << 2 | static_cast(ConsumeByte(page)); - MAYBE_PAGE_OVERFLOW(page); + bitmaskL2 = static_cast(timebinLinkHeaderStart & 0b11000000) << 2 | static_cast(ConsumeByte(ctx.page)); + MAYBE_PAGE_OVERFLOW(ctx.page); } int32_t nBytesBitmask = CAMath::Popcount(bitmaskL2); - assert(nBytesBitmask <= 10); - for (int32_t chan = iThread; chan < CAMath::nextMultipleOf(80); chan += NTHREADS) { + for (int32_t chan = ctx.iThread; chan < CAMath::nextMultipleOf(80); chan += NTHREADS) { int32_t chanL2Idx = chan / 8; bool l2 = TEST_BIT(bitmaskL2, chanL2Idx); int32_t chanByteOffset = nBytesBitmask - 1 - CAMath::Popcount(bitmaskL2 >> (chanL2Idx + 1)); - uint8_t myChannelHasData = (chan < 80 && l2 ? TEST_BIT(PEEK_OVERFLOW(page, chanByteOffset), chan % 8) : 0); - assert(myChannelHasData == 0 || myChannelHasData == 1); + uint8_t myChannelHasData = (chan < 80 && l2 ? TEST_BIT(PEEK_OVERFLOW(ctx.page, chanByteOffset), chan % 8) : 0); int32_t nSamplesStep; int32_t threadSampleOffset = CfUtils::warpPredicateScan(myChannelHasData, &nSamplesStep); @@ -768,10 +692,10 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( nSamplesInTB += nSamplesStep; } - ConsumeBytes(page, nBytesBitmask); - MAYBE_PAGE_OVERFLOW(page); + ConsumeBytes(ctx.page, nBytesBitmask); + MAYBE_PAGE_OVERFLOW(ctx.page); - if (iThread == 0) { + if (ctx.iThread == 0) { smem.samplesPerLinkEnd[iLink] = nSamplesInTB; } @@ -779,31 +703,37 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all writes to shared memory are finished, before reading it - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); // TODO: We don't need this check? + if (nSamplesInTB > nSamplesLeftInPage) { + return -GPUErrors::ERROR_TPCZS_INVALID_NADC; + } + + // This needs to happen BEFORE checking if the timebin is in fragment + // to ensure ADC bytes are always consumed, even if data isn't decoded + const uint8_t* adcData = ConsumeBytes(ctx.page, (nSamplesInTB * DECODE_BITS + 7) / 8); + MAYBE_PAGE_OVERFLOW(ctx.page); - if (not fragment.contains(timeBin)) { - return FillWithInvalid(clusterer, iThread, NTHREADS, pageDigitOffset, nSamplesInTB); + bool discardTimeBin = not fragment.contains(timeBin); + discardTimeBin |= (ctx.tpcTimeBinCut > 0 && timeBin > ctx.tpcTimeBinCut); + + if (discardTimeBin) { + return FillWithInvalid(ctx.clusterer, ctx.iThread, NTHREADS, ctx.pageDigitOffset, nSamplesInTB); } // Unpack ADC int32_t iLink = 0; - for (uint16_t sample = iThread; sample < nSamplesInTB; sample += NTHREADS) { + for (uint16_t sample = ctx.iThread; sample < nSamplesInTB; sample += NTHREADS) { const uint16_t adcBitOffset = sample * DECODE_BITS; uint16_t adcByteOffset = adcBitOffset / CHAR_BIT; const uint8_t adcOffsetInByte = adcBitOffset - adcByteOffset * CHAR_BIT; - uint8_t bits = 0; - uint16_t byte = 0; - static_assert(DECODE_BITS <= sizeof(uint16_t) * CHAR_BIT); - while (bits < DECODE_BITS) { - byte |= static_cast(PEEK_OVERFLOW(adcData, adcByteOffset)) << bits; + uint16_t adc = 0; + for (uint8_t bits = 0; bits < DECODE_BITS; bits += CHAR_BIT) { + adc |= static_cast(PEEK_OVERFLOW(adcData, adcByteOffset)) << bits; adcByteOffset++; - bits += CHAR_BIT; } - byte >>= adcOffsetInByte; + adc >>= adcOffsetInByte; while (smem.samplesPerLinkEnd[iLink] <= sample) { iLink++; @@ -812,18 +742,15 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( int32_t rawFECChannelLink = smem.rawFECChannels[sample]; // Unpack data for cluster finder - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannelLink, smem.linkIds[iLink]); + o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannelLink, smem.linkIds[iLink]); - float charge = ADCToFloat(byte, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(clusterer, charge, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + sample); + float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR); + WriteCharge(ctx.clusterer, charge, padAndRow, fragment.toLocal(timeBin), ctx.pageDigitOffset + sample); } // for (uint16_t sample = iThread; sample < nSamplesInTB; sample += NTHREADS) GPUbarrierWarp(); // Ensure all reads to shared memory are finished, before decoding next header into shmem - assert(PayloadExtendsToNextPage || adcData <= page); - assert(PayloadExtendsToNextPage || page <= payloadEnd); - return nSamplesInTB; #undef TEST_BIT @@ -831,121 +758,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( #undef MAYBE_PAGE_OVERFLOW } -template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( - processorType& clusterer, - const uint8_t*& page, - uint32_t pageDigitOffset, - const header::RAWDataHeader* rawDataHeader, - int32_t firstHBF, - int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) -{ -#define MAYBE_PAGE_OVERFLOW(pagePtr) \ - if constexpr (PayloadExtendsToNextPage) { \ - if (pagePtr >= payloadEnd && pagePtr < nextPage) { \ - ptrdiff_t diff = pagePtr - payloadEnd; \ - pagePtr = nextPage; \ - ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ - } \ - } else { \ - assert(pagePtr <= payloadEnd); \ - } - - using zerosupp_link_based::ChannelPerTBHeader; - - const CfFragment& fragment = clusterer.mPmemory->fragment; - - uint8_t linkIds[MaxNLinksPerTimebin]; - uint8_t channelMasks[MaxNLinksPerTimebin * 10] = {0}; - uint16_t nSamplesWritten = 0; - - // Read timebin block header - uint16_t tbbHdr = ConsumeByte(page); - MAYBE_PAGE_OVERFLOW(page); - tbbHdr |= static_cast(ConsumeByte(page)) << CHAR_BIT; - MAYBE_PAGE_OVERFLOW(page); - - uint8_t nLinksInTimebin = tbbHdr & 0x000F; - uint16_t linkBC = (tbbHdr & 0xFFF0) >> 4; - int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - firstHBF) * constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; - - uint16_t nSamplesInTB = 0; - - // Read timebin link headers - for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) { - uint8_t timebinLinkHeaderStart = ConsumeByte(page); - MAYBE_PAGE_OVERFLOW(page); - - linkIds[iLink] = timebinLinkHeaderStart & 0b00011111; - - bool bitmaskIsFlat = timebinLinkHeaderStart & 0b00100000; - - uint16_t bitmaskL2 = 0x0FFF; - if (not bitmaskIsFlat) { - bitmaskL2 = static_cast(timebinLinkHeaderStart & 0b11000000) << 2 | static_cast(ConsumeByte(page)); - MAYBE_PAGE_OVERFLOW(page); - } - - for (int32_t i = 0; i < 10; i++) { - if (bitmaskL2 & 1 << i) { - nSamplesInTB += CAMath::Popcount(*Peek(page)); - channelMasks[10 * iLink + i] = ConsumeByte(page); - MAYBE_PAGE_OVERFLOW(page); - } - } - - } // for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) - - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); - - if (not fragment.contains(timeBin)) { - FillWithInvalid(clusterer, 0, 1, pageDigitOffset, nSamplesInTB); - return nSamplesInTB; - } - - // Unpack ADC - uint32_t byte = 0, bits = 0; - uint16_t rawFECChannel = 0; - - // unpack adc values, assume tightly packed data - while (nSamplesWritten < nSamplesInTB) { - byte |= static_cast(ConsumeByte(adcData)) << bits; - MAYBE_PAGE_OVERFLOW(adcData); - bits += CHAR_BIT; - while (bits >= DECODE_BITS) { - - // Find next channel with data - for (; !ChannelIsActive(channelMasks, rawFECChannel); rawFECChannel++) { - } - - int32_t iLink = rawFECChannel / ChannelPerTBHeader; - int32_t rawFECChannelLink = rawFECChannel % ChannelPerTBHeader; - - // Unpack data for cluster finder - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannelLink, linkIds[iLink]); - - float charge = ADCToFloat(byte, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(clusterer, charge, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + nSamplesWritten); - - byte >>= DECODE_BITS; - bits -= DECODE_BITS; - nSamplesWritten++; - rawFECChannel++; // Ensure we don't decode same channel twice - } // while (bits >= DECODE_BITS) - } // while (nSamplesWritten < nAdc) - - assert(PayloadExtendsToNextPage || adcData <= page); - assert(PayloadExtendsToNextPage || page <= payloadEnd); - assert(nSamplesWritten == nSamplesInTB); - - return nSamplesWritten; - -#undef MAYBE_PAGE_OVERFLOW -} - GPUd() bool GPUTPCCFDecodeZSDenseLink::ChannelIsActive(const uint8_t* chan, uint16_t chanIndex) { constexpr uint8_t N_BITS_PER_ENTRY = sizeof(*chan) * CHAR_BIT; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index e476674e030f9..b8ff90f511057 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -21,7 +21,7 @@ #include "TPCBase/PadPos.h" #include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTPC/ZeroSuppressionLinkBased.h" -#include "DetectorsRaw/RDHUtils.h" +#include "Headers/RAWDataHeader.h" namespace o2::gpu { @@ -45,7 +45,7 @@ class GPUTPCCFDecodeZS : public GPUKernelTemplate decodeZS, }; - static GPUd() void decode(GPUTPCClusterFinder& clusterer, GPUSharedMemory& s, int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t firstHBF); + static GPUd() void decode(GPUTPCClusterFinder& clusterer, GPUSharedMemory& s, int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t firstHBF, int32_t tpcTimeBinCut); typedef GPUTPCClusterFinder processorType; GPUhdi() static processorType* Processor(GPUConstantMem& processors) @@ -53,9 +53,9 @@ class GPUTPCCFDecodeZS : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template @@ -72,13 +72,22 @@ class GPUTPCCFDecodeZSLinkBase : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } + struct DecodeCtx { + processorType& clusterer; + const uint8_t* page; + int32_t iBlock, nThreads, iThread; + uint32_t pageDigitOffset; + int32_t firstHBF; + int32_t tpcTimeBinCut; + }; + template - GPUd() static void Decode(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, typename Decoder::GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF); + GPUd() static void Decode(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, typename Decoder::GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut); GPUd() static o2::tpc::PadPos GetPadAndRowFromFEC(processorType& clusterer, int32_t cru, int32_t rawFecChannel, int32_t fecInPartition); GPUd() static void WriteCharge(processorType& clusterer, float charge, o2::tpc::PadPos pos, tpccf::TPCFragmentTime localTime, size_t positionOffset); @@ -134,13 +143,12 @@ class GPUTPCCFDecodeZSLink : public GPUTPCCFDecodeZSLinkBase template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); - GPUd() static size_t DecodePage(GPUSharedMemory& smem, processorType& clusterer, int32_t iBlock, int32_t nThreads, int32_t iThread, const uint8_t* page, uint32_t pageDigitOffset, int32_t firstHBF); + GPUd() static size_t DecodePage(GPUSharedMemory& smem, DecodeCtx& ctx); GPUd() static void GetChannelBitmask(const tpc::zerosupp_link_based::CommonHeader& tbHdr, uint32_t* chan); GPUd() static bool ChannelIsActive(const uint32_t* chan, uint8_t chanIndex); - GPUd() static void DecodeTBSingleThread(processorType& clusterer, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, int32_t fecInPartition, uint32_t pageDigitOffset); - GPUd() static void DecodeTBMultiThread(processorType& clusterer, int32_t iThread, GPUSharedMemory& smem, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, int32_t fecInPartition, uint32_t pageDigitOffset); + GPUd() static void DecodeTB(GPUSharedMemory& smem, DecodeCtx& ctx, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, int32_t fecInPartition); }; class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase @@ -163,18 +171,15 @@ class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); - GPUd() static uint32_t DecodePage(GPUSharedMemory& smem, processorType& clusterer, int32_t iBlock, int32_t nThreads, int32_t iThread, const uint8_t* page, uint32_t pageDigitOffset, int32_t firstHBF); + GPUd() static uint32_t DecodePage(GPUSharedMemory& smem, DecodeCtx& ctx); GPUd() static bool ChannelIsActive(const uint8_t* chan, uint16_t chanIndex); - template - GPUd() static uint16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); - - template - GPUd() static uint16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); - + // Decode a single timebin within an 8kb page. + // Returns the number of samples decoded from the page + // or negative value to indicate an error (no samples are written in this case) template - GPUd() static uint16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTB(GPUSharedMemory& smem, DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h index 902e3a28fd21b..2debce3dc0d6c 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h @@ -42,9 +42,9 @@ class GPUTPCCFDeconvolution : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h index 210853237b86e..50fbe63eb6dac 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h @@ -32,9 +32,9 @@ class GPUTPCCFGather : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h index 6bdec7760527c..cc39938b70d21 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h @@ -44,9 +44,9 @@ class GPUTPCCFMCLabelFlattener : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h index 59196da11079b..27095bb17c1e9 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h @@ -48,9 +48,9 @@ class GPUTPCCFNoiseSuppression : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h index e480518ddc9dd..eeda0cecb3bc3 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h @@ -42,9 +42,9 @@ class GPUTPCCFPeakFinder : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h index a5ea8b24e9522..3b9b7e2b8329a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h @@ -51,9 +51,9 @@ class GPUTPCCFStreamCompaction : public GPUKernelTemplate return processors.tpcClusterer; } - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } template diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx index 541edaa689c6c..44b005eb20233 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx @@ -48,7 +48,7 @@ void* GPUTPCClusterFinder::SetPointersMemory(void* mem) void* GPUTPCClusterFinder::SetPointersInput(void* mem) { - if (mNMaxPages == 0 && (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding)) { + if (mNMaxPages == 0 && (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding)) { computePointerWithAlignment(mem, mPdigits, mNMaxDigits); } return mem; @@ -56,7 +56,7 @@ void* GPUTPCClusterFinder::SetPointersInput(void* mem) void* GPUTPCClusterFinder::SetPointersZSOffset(void* mem) { - const int32_t n = (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding) ? mNMaxPages : GPUTrackingInOutZS::NENDPOINTS; + const int32_t n = (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding) ? mNMaxPages : GPUTrackingInOutZS::NENDPOINTS; if (n) { computePointerWithAlignment(mem, mPzsOffsets, n); } @@ -65,7 +65,7 @@ void* GPUTPCClusterFinder::SetPointersZSOffset(void* mem) void* GPUTPCClusterFinder::SetPointersZS(void* mem) { - if (mNMaxPages && (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding)) { + if (mNMaxPages && (mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding)) { computePointerWithAlignment(mem, mPzs, mNMaxPages * TPCZSHDR::TPC_ZS_PAGE_SIZE); } return mem; @@ -92,7 +92,7 @@ void* GPUTPCClusterFinder::SetPointersScratch(void* mem) computePointerWithAlignment(mem, mPchargeMap, TPCMapMemoryLayout::items(mRec->GetProcessingSettings().overrideClusterizerFragmentLen)); computePointerWithAlignment(mem, mPpeakMap, TPCMapMemoryLayout::items(mRec->GetProcessingSettings().overrideClusterizerFragmentLen)); computePointerWithAlignment(mem, mPclusterByRow, GPUCA_ROW_COUNT * mNMaxClusterPerRow); - if ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding)) { + if ((mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding)) { computePointerWithAlignment(mem, mPscanBuf, mBufSize * mNBufs); } return mem; @@ -131,15 +131,15 @@ void GPUTPCClusterFinder::SetMaxData(const GPUTrackingInOutPointers& io) if (mRec->GetProcessingSettings().tpcIncreasedMinClustersPerRow) { mNMaxClusterPerRow = std::max(mNMaxClusterPerRow, mRec->GetProcessingSettings().tpcIncreasedMinClustersPerRow); } - if ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding)) { - mBufSize = nextMultipleOf(mNMaxDigitsFragment, std::max(GPUCA_MEMALIGN, mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding).par_CF_SCAN_WORKGROUP_SIZE)); + if ((mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding)) { + mBufSize = nextMultipleOf(mNMaxDigitsFragment, std::max(GPUCA_MEMALIGN, mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding).par_CF_SCAN_WORKGROUP_SIZE)); mNBufs = getNSteps(mBufSize); } } void GPUTPCClusterFinder::SetNMaxDigits(size_t nDigits, size_t nPages, size_t nDigitsFragment, size_t nDigitsEndpointMax) { - mNMaxDigits = nextMultipleOf(nDigits, std::max(GPUCA_MEMALIGN, mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding).par_CF_SCAN_WORKGROUP_SIZE)); + mNMaxDigits = nextMultipleOf(nDigits, std::max(GPUCA_MEMALIGN, mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding).par_CF_SCAN_WORKGROUP_SIZE)); mNMaxPages = nPages; mNMaxDigitsFragment = nDigitsFragment; mNMaxDigitsEndpoint = nDigitsEndpointMax; @@ -151,7 +151,7 @@ uint32_t GPUTPCClusterFinder::getNSteps(size_t items) const return 0; } uint32_t c = 1; - const size_t scanWorkgroupSize = mRec->getGPUParameters(mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding).par_CF_SCAN_WORKGROUP_SIZE; + const size_t scanWorkgroupSize = mRec->getGPUParameters(mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCClusterFinding).par_CF_SCAN_WORKGROUP_SIZE; size_t capacity = scanWorkgroupSize; while (items > capacity) { capacity *= scanWorkgroupSize; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h index 35e2a7297338f..6958134d7d716 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h @@ -17,7 +17,7 @@ #include "GPUDef.h" #include "GPUProcessor.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "CfFragment.h" namespace o2 diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizer.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizer.h index 0b9553437765c..b7bc1575d349a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizer.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizer.h @@ -71,6 +71,7 @@ class GPUTPCNNClusterizer : public GPUProcessor uint32_t mNnClusterizerPadTimeSize = 0; uint32_t mNnClusterizerRowTimeSize = 0; uint32_t mNnClusterizerRowTimeSizeFull = 0; + uint32_t mNnClusterizerRowTimeSizeThreads = 0; // Boundary lookup table // int32_t mBoundaryMapSizeRow = 0; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx index ae833ace2f648..77d5ee13f85fb 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx @@ -36,7 +36,7 @@ void GPUTPCNNClusterizerHost::init(const GPUSettingsProcessingNNclusterizer& set std::vector evalMode = o2::utils::Str::tokenize(settings.nnEvalMode, ':'); if (settings.nnLoadFromCCDB) { - reg_model_path = settings.nnLocalFolder + "/net_regression_c1.onnx"; // Needs to be set identical to NeuralNetworkClusterizer.cxx, otherwise the networks might be loaded from the wrong place + reg_model_path = settings.nnLocalFolder + "/net_regression_c1.onnx"; // Needs to be set identical to GPUWorkflowSpec.cxx, otherwise the networks might be loaded from the wrong place if (evalMode[0] == "c1") { class_model_path = settings.nnLocalFolder + "/net_classification_c1.onnx"; } else if (evalMode[0] == "c2") { @@ -98,6 +98,7 @@ void GPUTPCNNClusterizerHost::initClusterizer(const GPUSettingsProcessingNNclust clustererNN.mNnClusterizerPadTimeSize = clustererNN.mNnClusterizerFullPadSize * clustererNN.mNnClusterizerFullTimeSize; clustererNN.mNnClusterizerRowTimeSize = clustererNN.mNnClusterizerFullRowSize * clustererNN.mNnClusterizerFullTimeSize; clustererNN.mNnClusterizerRowTimeSizeFull = clustererNN.mNnClusterizerRowTimeSize + (settings.nnClusterizerAddIndexData ? 3 : 0); + clustererNN.mNnClusterizerRowTimeSizeThreads = clustererNN.mNnClusterizerRowTimeSize + (settings.nnClusterizerAddIndexData ? 1 : 0); clustererNN.mNnClusterizerElementSize = clustererNN.mNnClusterizerChargeArraySize + (settings.nnClusterizerAddIndexData ? 3 : 0); // clustererNN.mBoundaryMapSizeRow = 3 * clustererNN.mNnClusterizerSizeInputRow + o2::tpc::constants::MAXGLOBALPADROW; // clustererNN.mBoundaryPadding = 11; // padding on each side to account for pad_offset. N=11 since then mIsBoundary = 24320 ~< (1.5 x 2^14 = 24576) && N must be bigger than (NPads[row(end_iroc + 1)] - NPads[row(end_iroc)])/2 (=6) for pad_offset to work diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.cxx index 55fefa7dcf149..ee0fa217b8095 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.cxx @@ -57,7 +57,7 @@ GPUdii() void GPUTPCNNClusterizerKernels::Thread= clusterer.mPmemory->counters.nClusters || glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) { return; } @@ -67,39 +67,42 @@ GPUdii() void GPUTPCNNClusterizerKernels::Thread chargeMap(reinterpret_cast(clusterer.mPchargeMap)); CfArray2D isPeakMap(clusterer.mPpeakMap); CfChargePos peak = clusterer.mPfilteredPeakPositions[CAMath::Min(glo_idx + batchStart, (uint32_t)(clusterer.mPmemory->counters.nClusters - 1))]; - int32_t row = static_cast(peak.row()); - int32_t pad = static_cast(peak.pad()); - int32_t time = static_cast(peak.time()); - float central_charge = static_cast(chargeMap[peak].unpack()); - int32_t row_offset = GPUTPCNNClusterizerKernels::rowOffset(row, clustererNN.mNnClusterizerSizeInputRow); - - for (int32_t r = -clustererNN.mNnClusterizerSizeInputRow; r <= clustererNN.mNnClusterizerSizeInputRow; ++r) { - int32_t target_row = row + r; - bool is_row_boundary = (target_row < 0) || (target_row >= o2::tpc::constants::MAXGLOBALPADROW); - int32_t pad_offset = is_row_boundary ? 0 : GPUTPCNNClusterizerKernels::padOffset(row, target_row); - - for (int32_t p = -clustererNN.mNnClusterizerSizeInputPad + pad_offset; p <= clustererNN.mNnClusterizerSizeInputPad + pad_offset; ++p) { - int32_t target_pad = pad + p; - bool is_boundary = is_row_boundary || GPUTPCNNClusterizerKernels::isBoundary(target_row + row_offset, target_pad, clustererNN.mNnClusterizerSizeInputRow); - - for (int32_t t = -clustererNN.mNnClusterizerSizeInputTime; t <= clustererNN.mNnClusterizerSizeInputTime; ++t) { - int32_t target_time = time + t; - + const int32_t row = static_cast(peak.row()); + const int32_t pad = static_cast(peak.pad()); + const int32_t time = static_cast(peak.time()); + const float central_charge = static_cast(chargeMap[peak].unpack()); + const float inverse_charge = 1.f / central_charge; + + const int32_t row_offset = GPUTPCNNClusterizerKernels::rowOffset(row, clustererNN.mNnClusterizerSizeInputRow); + const int32_t iroc_row = 63 + clustererNN.mNnClusterizerSizeInputRow; + const int32_t maxrow = o2::tpc::constants::MAXGLOBALPADROW + clustererNN.mNnClusterizerSizeInputRow; + const int32_t npads_row = GPUTPCGeometry::NPads(row); + float output_value = clustererNN.mNnClusterizerBoundaryFillValue; + + for (int32_t target_row = -clustererNN.mNnClusterizerSizeInputRow + row; target_row <= clustererNN.mNnClusterizerSizeInputRow + row; ++target_row) { + uint8_t is_boundary = (target_row < 0) || (target_row >= o2::tpc::constants::MAXGLOBALPADROW); + const int32_t p_local = pad + (is_boundary ? 0 : GPUTPCNNClusterizerKernels::padOffset(row, target_row)); + const int32_t npads_reference = is_boundary ? 0 : GPUTPCGeometry::NPads(target_row - row_offset); + + for (int32_t target_pad = -clustererNN.mNnClusterizerSizeInputPad + p_local; target_pad <= clustererNN.mNnClusterizerSizeInputPad + p_local; ++target_pad) { + is_boundary = is_boundary || GPUTPCNNClusterizerKernels::isBoundary(target_row + row_offset, target_pad, maxrow, iroc_row, npads_row, npads_reference); + + for (int32_t target_time = -clustererNN.mNnClusterizerSizeInputTime + time; target_time <= clustererNN.mNnClusterizerSizeInputTime + time; ++target_time) { if (is_boundary || target_time < 0 || target_time >= clustererNN.maxAllowedTimebin) { // Fill boundary value - float boundary_value = static_cast(clustererNN.mNnClusterizerBoundaryFillValue); + output_value = clustererNN.mNnClusterizerBoundaryFillValue; if (dtype == 0) { - clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)boundary_value; + clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value; } else { - clustererNN.mInputData_32[write_idx] = boundary_value; + clustererNN.mInputData_32[write_idx] = output_value; } } else { CfChargePos tmp_pos(target_row, target_pad, target_time); - float normalized_charge = static_cast(chargeMap[tmp_pos].unpack()) / central_charge; + output_value = chargeMap[tmp_pos].unpack() * inverse_charge; if (dtype == 0) { - clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)normalized_charge; + clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value; } else { - clustererNN.mInputData_32[write_idx] = normalized_charge; + clustererNN.mInputData_32[write_idx] = output_value; } } // if((CAMath::Abs(static_cast(clustererNN.mInputData_16_Test[write_idx]) - static_cast(clustererNN.mInputData_16[write_idx])) > 1e-4) && ((glo_idx + batchStart) < clusterer.mPmemory->counters.nClusters)) { @@ -115,11 +118,11 @@ GPUdii() void GPUTPCNNClusterizerKernels::Thread(sector) / o2::tpc::constants::MAXSECTOR); clustererNN.mInputData_16[write_idx + 1] = (OrtDataType::Float16_t)(static_cast(row) / o2::tpc::constants::MAXGLOBALPADROW); - clustererNN.mInputData_16[write_idx + 2] = (OrtDataType::Float16_t)(static_cast(pad) / GPUTPCGeometry::NPads(row)); + clustererNN.mInputData_16[write_idx + 2] = (OrtDataType::Float16_t)(static_cast(pad) / npads_row); } else { clustererNN.mInputData_32[write_idx] = static_cast(sector) / o2::tpc::constants::MAXSECTOR; clustererNN.mInputData_32[write_idx + 1] = static_cast(row) / o2::tpc::constants::MAXGLOBALPADROW; - clustererNN.mInputData_32[write_idx + 2] = static_cast(pad) / GPUTPCGeometry::NPads(row); + clustererNN.mInputData_32[write_idx + 2] = static_cast(pad) / npads_row; } } @@ -139,16 +142,16 @@ GPUdii() void GPUTPCNNClusterizerKernels::Thread GPUdii() void GPUTPCNNClusterizerKernels::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart) { - uint32_t glo_idx = get_global_id(0); + const uint32_t glo_idx = get_global_id(0); auto& clusterer = processors.tpcClusterer[sector]; auto& clustererNN = processors.tpcNNClusterer[sector]; - if (glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode * clustererNN.mNnClusterizerRowTimeSizeFull) { + if (glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode * clustererNN.mNnClusterizerRowTimeSizeThreads) { return; } - uint32_t base_idx = glo_idx / clustererNN.mNnClusterizerRowTimeSizeFull; - uint32_t transient_index = glo_idx - (base_idx * clustererNN.mNnClusterizerRowTimeSizeFull); + const uint32_t base_idx = glo_idx / clustererNN.mNnClusterizerRowTimeSizeThreads; + const uint32_t transient_index = glo_idx - (base_idx * clustererNN.mNnClusterizerRowTimeSizeThreads); // Early exit for out-of-bounds threads if (base_idx + batchStart >= clusterer.mPmemory->counters.nClusters) { @@ -160,108 +163,74 @@ GPUdii() void GPUTPCNNClusterizerKernels::Threadcounters.nClusters - 1))]; - float central_charge = static_cast(chargeMap[peak].unpack()); - int32_t row = static_cast(peak.row()); - int32_t pad = static_cast(peak.pad()); - int32_t time = static_cast(peak.time()); + const float central_charge = chargeMap[peak].unpack(); + const int32_t row = static_cast(peak.row()); + const int32_t pad = static_cast(peak.pad()); + const int32_t time = static_cast(peak.time()); // Handle index data with fewer branches if (clustererNN.mNnClusterizerAddIndexData && transient_index >= clustererNN.mNnClusterizerRowTimeSize) { - int32_t data_idx = transient_index - clustererNN.mNnClusterizerRowTimeSize; - uint32_t write_idx = base_idx * clustererNN.mNnClusterizerElementSize + clustererNN.mNnClusterizerChargeArraySize + data_idx; - - float index_values[3] = { - static_cast(sector) / o2::tpc::constants::MAXSECTOR, - static_cast(row) / o2::tpc::constants::MAXGLOBALPADROW, - static_cast(pad) / GPUTPCGeometry::NPads(row)}; - + uint32_t write_idx = base_idx * clustererNN.mNnClusterizerElementSize + clustererNN.mNnClusterizerChargeArraySize; + const int32_t npads = GPUTPCGeometry::NPads(row); if (dtype == 0) { - clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)index_values[data_idx]; + clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)(static_cast(sector) / o2::tpc::constants::MAXSECTOR); + clustererNN.mInputData_16[write_idx + 1] = (OrtDataType::Float16_t)(static_cast(row) / o2::tpc::constants::MAXGLOBALPADROW); + clustererNN.mInputData_16[write_idx + 2] = (OrtDataType::Float16_t)(static_cast(pad) / npads); } else { - clustererNN.mInputData_32[write_idx] = index_values[data_idx]; - } - - // Handle deconvolution flags only once per cluster (last thread in element) - if (!clustererNN.mNnClusterizerSetDeconvolutionFlags && data_idx == 2) { - uint8_t cluster_flags = 0; - for (uint16_t i = 0; i < 8; i++) { - Delta2 d = cfconsts::InnerNeighbors[i]; - CfChargePos tmp_pos = peak.delta(d); - cluster_flags += CfUtils::isPeak(isPeakMap[tmp_pos]); - } - clustererNN.mClusterFlags[2 * base_idx] = cluster_flags; - clustererNN.mClusterFlags[2 * base_idx + 1] = cluster_flags; + clustererNN.mInputData_32[write_idx] = static_cast(sector) / o2::tpc::constants::MAXSECTOR; + clustererNN.mInputData_32[write_idx + 1] = static_cast(row) / o2::tpc::constants::MAXGLOBALPADROW; + clustererNN.mInputData_32[write_idx + 2] = static_cast(pad) / npads; } - return; } // Main data processing - optimize index calculations if (transient_index < clustererNN.mNnClusterizerRowTimeSize) { // Optimize 3D index calculation - int32_t row_idx = transient_index / clustererNN.mNnClusterizerFullTimeSize; - int32_t r_local = row_idx - clustererNN.mNnClusterizerSizeInputRow; - int32_t time_idx = transient_index - row_idx * clustererNN.mNnClusterizerFullTimeSize; - int32_t t_local = time_idx - clustererNN.mNnClusterizerSizeInputTime; + const int32_t row_idx = transient_index / clustererNN.mNnClusterizerFullTimeSize; + const int32_t time_idx = transient_index - row_idx * clustererNN.mNnClusterizerFullTimeSize; int32_t write_idx = base_idx * clustererNN.mNnClusterizerElementSize + row_idx * clustererNN.mNnClusterizerPadTimeSize + time_idx; // Early boundary check for row - int32_t target_row = row + r_local; - int8_t is_row_boundary = (target_row < 0) || (target_row > (o2::tpc::constants::MAXGLOBALPADROW - 1)); - - // Calculate offsets - int32_t row_offset = GPUTPCNNClusterizerKernels::rowOffset(row, clustererNN.mNnClusterizerSizeInputRow); - int32_t pad_offset = GPUTPCNNClusterizerKernels::padOffset(row, target_row); - for (int32_t p_local = -clustererNN.mNnClusterizerSizeInputPad + pad_offset; p_local <= clustererNN.mNnClusterizerSizeInputPad + pad_offset; p_local++) { - if (is_row_boundary) { - // Use boundary fill value - float boundary_val = static_cast(clustererNN.mNnClusterizerBoundaryFillValue); + const int32_t target_row = row + row_idx - clustererNN.mNnClusterizerSizeInputRow; + float output_value = clustererNN.mNnClusterizerBoundaryFillValue; + + if ((row < 63 && target_row > 62) || (target_row < 0) || (row > 62 && target_row < 63) || (target_row >= o2::tpc::constants::MAXGLOBALPADROW)) { + for (uint32_t target_pad = 0; target_pad < clustererNN.mNnClusterizerFullPadSize; ++target_pad) { if (dtype == 0) { - clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)boundary_val; + clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value; } else { - clustererNN.mInputData_32[write_idx] = boundary_val; + clustererNN.mInputData_32[write_idx] = output_value; } - write_idx += clustererNN.mNnClusterizerFullTimeSize; // Move to next pad position - continue; - } - - // Calculate target pad and time - int32_t target_pad = pad + p_local; - int32_t target_time = time + t_local; - - // Optimized boundary check - int8_t is_boundary = GPUTPCNNClusterizerKernels::isBoundary(target_row + row_offset, target_pad, clustererNN.mNnClusterizerSizeInputRow) || (target_time < 0) || (target_time >= clustererNN.maxAllowedTimebin); - - float output_value; - if (is_boundary) { - output_value = static_cast(clustererNN.mNnClusterizerBoundaryFillValue); - } else { - // Coalesced memory access - create position and read charge - CfChargePos tmp_pos(target_row, target_pad, target_time); - output_value = static_cast(chargeMap[tmp_pos].unpack()) / central_charge; // Normalize by central charge + write_idx += clustererNN.mNnClusterizerFullTimeSize; } - - // Write output with reduced branching - if (dtype == 0) { - clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value; - } else { - clustererNN.mInputData_32[write_idx] = output_value; + return; + } else { + // Calculate offsets + const int32_t target_time = time + time_idx - clustererNN.mNnClusterizerSizeInputTime; + const uint8_t is_time_boundary = (target_time < 0) || (target_time >= clustererNN.maxAllowedTimebin); + const float inverse_central_charge = 1.f / central_charge; // multiply by inverse is cheaper than divide + const int32_t p_local = pad + GPUTPCNNClusterizerKernels::padOffset(row, target_row); + const int32_t npads = GPUTPCGeometry::NPads(target_row); + + const int32_t start_pad = -clustererNN.mNnClusterizerSizeInputPad + p_local; + const int32_t end_pad = clustererNN.mNnClusterizerSizeInputPad + p_local; + + for (int32_t target_pad = start_pad; target_pad <= end_pad; ++target_pad) { + if (target_pad >= npads || target_pad < 0 || is_time_boundary) { + output_value = clustererNN.mNnClusterizerBoundaryFillValue; + } else { + CfChargePos pos(target_row, target_pad, target_time); + // one load + one multiply + output_value = chargeMap[pos].unpack() * inverse_central_charge; + } + if (dtype == 0) { + clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value; + } else { + clustererNN.mInputData_32[write_idx] = output_value; + } + write_idx += clustererNN.mNnClusterizerFullTimeSize; } - // if (write_idx >= clustererNN.mNnClusterizerElementSize * clustererNN.mNnClusterizerBatchedMode) { - // printf("Error: Write index out of bounds (central array)! %d >= %d (write_idx: %d, base_idx: %d, transient_index: %d, row_idx: %d, time_idx: %d, r_local: %d, t_local: %d)\n", - // write_idx, (int)(clustererNN.mNnClusterizerElementSize * clustererNN.mNnClusterizerBatchedMode), write_idx, base_idx, transient_index, row_idx, time_idx, r_local, t_local); - // } - // if ((clusterer.mPmemory->counters.nClusters - batchStart) < clustererNN.mNnClusterizerBatchedMode) { - // if (write_idx >= ((clusterer.mPmemory->counters.nClusters - batchStart) * clustererNN.mNnClusterizerElementSize)) { - // printf("Error: Write index out of bounds (end of array)! %d >= %d (write_idx: %d, base_idx: %d, transient_index: %d, row_idx: %d, time_idx: %d, r_local: %d, t_local: %d)\n", - // write_idx, (int)((clusterer.mPmemory->counters.nClusters - batchStart) * clustererNN.mNnClusterizerElementSize), write_idx, base_idx, transient_index, row_idx, time_idx, r_local, t_local); - // } - // if (write_idx > ((clusterer.mPmemory->counters.nClusters - batchStart) * clustererNN.mNnClusterizerElementSize - 5)) { - // printf("Sanity check (should appear only once) %d == %d (write_idx: %d, base_idx: %d, transient_index: %d, row_idx: %d, time_idx: %d, r_local: %d, t_local: %d)\n", - // write_idx, (int)((clusterer.mPmemory->counters.nClusters - batchStart) * clustererNN.mNnClusterizerElementSize - 4), write_idx, base_idx, transient_index, row_idx, time_idx, r_local, t_local); - // } - // } - - write_idx += clustererNN.mNnClusterizerFullTimeSize; // Move to next pad position + return; } } } @@ -737,16 +706,16 @@ GPUd() int32_t GPUTPCNNClusterizerKernels::rowOffset(int32_t row, int32_t offset return (row > 62 ? offset : 0); } -GPUd() bool GPUTPCNNClusterizerKernels::isBoundary(int32_t row, int32_t pad, int32_t offset) +GPUd() bool GPUTPCNNClusterizerKernels::isBoundary(int32_t row, int32_t pad, int32_t maxrow, int32_t iroc_row, int32_t npads_row, int32_t npads_reference) { - if (pad < 0 || row < 0) { // Faster short-circuit + if (pad < 0) { // Faster short-circuit return true; } else if (row < 63) { - return (pad >= static_cast(GPUTPCGeometry::NPads(row))); - } else if (row < (63 + offset)) { // to account for the gap between IROC and OROC. Charge will be set to the boundary fill value in order to signal boundaries to the neural network + return (pad >= npads_row); + } else if (row < iroc_row) { // to account for the gap between IROC and OROC. Charge will be set to the boundary fill value in order to signal boundaries to the neural network return true; - } else if (row < (o2::tpc::constants::MAXGLOBALPADROW + offset)) { - return (pad >= static_cast(GPUTPCGeometry::NPads(row - offset))); + } else if (row < maxrow) { + return (pad >= npads_reference); } else { return true; } diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.h index cd3d7783771fe..c77a99bec3a70 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.h @@ -44,9 +44,9 @@ class GPUTPCNNClusterizerKernels : public GPUKernelTemplate uint8_t innerAboveThreshold[SCRATCH_PAD_WORK_GROUP_SIZE]; }; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { - return GPUDataTypes::RecoStep::TPCClusterFinding; + return gpudatatypes::RecoStep::TPCClusterFinding; } enum K : int32_t { @@ -57,7 +57,7 @@ class GPUTPCNNClusterizerKernels : public GPUKernelTemplate determineClass2Labels = 4, publishClass1Regression = 5, publishClass2Regression = 6, - publishDeconvolutionFlags = 7, + publishDeconvolutionFlags = 7 }; template @@ -66,7 +66,7 @@ class GPUTPCNNClusterizerKernels : public GPUKernelTemplate private: static GPUd() int32_t padOffset(int32_t, int32_t); static GPUd() int32_t rowOffset(int32_t, int32_t); - static GPUd() bool isBoundary(int32_t, int32_t, int32_t); + static GPUd() bool isBoundary(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); static GPUd() bool isBoundaryPublish(int32_t, int32_t, float&, float&); }; diff --git a/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h b/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h index dd9a74f9b9131..b185a01a3f392 100644 --- a/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h +++ b/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h @@ -28,7 +28,7 @@ class GPUTPCConvertImpl public: GPUd() static void convert(const GPUConstantMem& GPUrestrict() cm, int32_t sector, int32_t row, float pad, float time, float& GPUrestrict() x, float& GPUrestrict() y, float& GPUrestrict() z) { - if (cm.param.par.continuousTracking) { + if (cm.param.par.continuousTracking) { // TODO: This might be wrong, don't we just need to do TransformInTimeframe always cm.calibObjects.fastTransformHelper->getCorrMap()->TransformInTimeFrame(sector, row, pad, time, x, y, z, cm.param.continuousMaxTimeBin); } else { cm.calibObjects.fastTransformHelper->Transform(sector, row, pad, time, x, y, z); diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx index 2f754d2416bc1..d5d400e30df53 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx @@ -23,6 +23,7 @@ #include "GPUCommonMath.h" #include "GPUCommonAlgorithm.h" #include "GPUConstantMem.h" +#include "GPUTRDRecoParam.h" using namespace o2::gpu; @@ -92,7 +93,7 @@ void* GPUTRDTracker_t::SetPointersTracks(void* base) } template -GPUTRDTracker_t::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRPhiA2(0), mRPhiB(0), mRPhiC2(0), mDyA2(0), mDyB(0), mDyC2(0), mAngleToDyA(0), mAngleToDyB(0), mAngleToDyC(0), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mZCorrCoefNRC(1.4f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) +GPUTRDTracker_t::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRecoParam(nullptr), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mZCorrCoefNRC(1.4f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) { //-------------------------------------------------------------------- // Default constructor @@ -114,9 +115,8 @@ void GPUTRDTracker_t::InitializeProcessor() //-------------------------------------------------------------------- // Initialise tracker //-------------------------------------------------------------------- - + mRecoParam = GetConstantMem()->calibObjects.trdRecoParam; UpdateGeometry(); - mDebug->ExpandVectors(); mIsInitialized = true; } @@ -131,42 +131,6 @@ void GPUTRDTracker_t::UpdateGeometry() if (!mGeo) { GPUFatal("TRD geometry must be provided externally"); } - float Bz = Param().bzkG; - float resRPhiIdeal2 = Param().rec.trd.trkltResRPhiIdeal * Param().rec.trd.trkltResRPhiIdeal; - GPUInfo("Initializing with B-field: %f kG", Bz); - if (CAMath::Abs(CAMath::Abs(Bz) - 2) < 0.1f) { - // magnetic field +-0.2 T - if (Bz > 0) { - GPUInfo("Loading error parameterization for Bz = +2 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = -1.43e-2f, mRPhiC2 = 4.55e-2f; - mDyA2 = 1.225e-3f, mDyB = -9.8e-3f, mDyC2 = 3.88e-2f; - mAngleToDyA = -0.1f, mAngleToDyB = 1.89f, mAngleToDyC = -0.4f; - } else { - GPUInfo("Loading error parameterization for Bz = -2 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = 1.43e-2f, mRPhiC2 = 4.55e-2f; - mDyA2 = 1.225e-3f, mDyB = 9.8e-3f, mDyC2 = 3.88e-2f; - mAngleToDyA = 0.1f, mAngleToDyB = 1.89f, mAngleToDyC = 0.4f; - } - } else if (CAMath::Abs(CAMath::Abs(Bz) - 5) < 0.1f) { - // magnetic field +-0.5 T - if (Bz > 0) { - GPUInfo("Loading error parameterization for Bz = +5 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = 0.125f, mRPhiC2 = 0.0961f; - mDyA2 = 1.681e-3f, mDyB = 0.15f, mDyC2 = 0.1849f; - mAngleToDyA = 0.13f, mAngleToDyB = 2.43f, mAngleToDyC = -0.58f; - } else { - GPUInfo("Loading error parameterization for Bz = -5 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = -0.14f, mRPhiC2 = 0.1156f; - mDyA2 = 2.209e-3f, mDyB = -0.15f, mDyC2 = 0.2025f; - mAngleToDyA = -0.15f, mAngleToDyB = 2.34f, mAngleToDyC = 0.56f; - } - } else { - // magnetic field 0 T or another value which is not covered by the error parameterizations - // using default values instead - GPUWarning("No error parameterization available for Bz = %.2f kG. Keeping default value (sigma_y = const. = 1cm)", Bz); - mRPhiA2 = 1.f; - } - // obtain average radius of TRD chambers float x0[kNLayers] = {300.2f, 312.8f, 325.4f, 338.0f, 350.6f, 363.2f}; // used as default value in case no transformation matrix can be obtained auto* matrix = mGeo->GetClusterMatrix(0); @@ -967,7 +931,7 @@ GPUd() void GPUTRDTracker_t::RecalcTrkltCov(const float tilt, cons //-------------------------------------------------------------------- float t2 = tilt * tilt; // tan^2 (tilt) float c2 = 1.f / (1.f + t2); // cos^2 (tilt) - float sy2 = GetRPhiRes(snp); + float sy2 = mRecoParam->getRPhiRes(snp); float sz2 = rowSize * rowSize / 12.f; cov[0] = c2 * (sy2 + t2 * sz2); cov[1] = c2 * tilt * (sz2 - sy2); @@ -977,8 +941,8 @@ GPUd() void GPUTRDTracker_t::RecalcTrkltCov(const float tilt, cons template GPUd() float GPUTRDTracker_t::GetAngularPull(float dYtracklet, float snp) const { - float dYtrack = ConvertAngleToDy(snp); - float dYresolution = GetAngularResolution(snp); + float dYtrack = mRecoParam->convertAngleToDy(snp); + float dYresolution = mRecoParam->getDyRes(snp); if (dYresolution < 1e-6f) { return 999.f; } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h index f8fa0342ee62d..5d7530ccecc11 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h @@ -38,6 +38,7 @@ class GPUTRDGeometry; class GPUChainTracking; template class GPUTRDTrackerDebug; +class GPUTRDRecoParam; //------------------------------------------------------------------------- template @@ -114,9 +115,6 @@ class GPUTRDTracker_t : public GPUProcessor GPUd() bool AdjustSector(PROP* prop, TRDTRK* t) const; GPUd() int32_t GetSector(float alpha) const; GPUd() float GetAlphaOfSector(const int32_t sec) const; - GPUd() float GetRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mRPhiB) * (snp - mRPhiB)); } // parametrization obtained from track-tracklet residuals: - GPUd() float GetAngularResolution(float snp) const { return mDyA2 + mDyC2 * (snp - mDyB) * (snp - mDyB); } // a^2 + c^2 * (snp - b)^2 - GPUd() float ConvertAngleToDy(float snp) const { return mAngleToDyA + mAngleToDyB * snp + mAngleToDyC * snp * snp; } // a + b*snp + c*snp^2 is more accurate than sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) GPUd() float GetAngularPull(float dYtracklet, float snp) const; GPUd() void RecalcTrkltCov(const float tilt, const float snp, const float rowSize, float (&cov)[3]); GPUd() void FindChambersInRoad(const TRDTRK* t, const float roadY, const float roadZ, const int32_t iLayer, int32_t* det, const float zMax, const float alpha, const float zShiftTrk) const; @@ -174,16 +172,7 @@ class GPUTRDTracker_t : public GPUProcessor TRDTRK* mCandidates; // array of tracks for multiple hypothesis tracking GPUTRDSpacePoint* mSpacePoints; // array with tracklet coordinates in global tracking frame const GPUTRDGeometry* mGeo; // TRD geometry - /// ---- error parametrization depending on magnetic field ---- - float mRPhiA2; // parameterization for tracklet position resolution - float mRPhiB; // parameterization for tracklet position resolution - float mRPhiC2; // parameterization for tracklet position resolution - float mDyA2; // parameterization for tracklet angular resolution - float mDyB; // parameterization for tracklet angular resolution - float mDyC2; // parameterization for tracklet angular resolution - float mAngleToDyA; // parameterization for conversion track angle -> tracklet deflection - float mAngleToDyB; // parameterization for conversion track angle -> tracklet deflection - float mAngleToDyC; // parameterization for conversion track angle -> tracklet deflection + const GPUTRDRecoParam* mRecoParam; // TRD RecoParam /// ---- end error parametrization ---- bool mDebugOutput; // store debug output static constexpr const float sRadialOffset = -0.1f; // due to (possible) mis-calibration of t0 -> will become obsolete when tracklet conversion is done outside of the tracker diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.h b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.h index 21135ddc48dfa..f9d500a72597a 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.h @@ -26,7 +26,7 @@ class GPUTRDTrackerKernels : public GPUKernelTemplate enum K { defaultKernel = 0, gpuVersion = 0, o2Version = 1 }; - GPUhdi() constexpr static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TRDTracking; } + GPUhdi() constexpr static gpudatatypes::RecoStep GetRecoStep() { return gpudatatypes::RecoStep::TRDTracking; } template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors, T* externalInstance = nullptr); }; diff --git a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C index 3d86a77b6d9cc..e4b37500e1a60 100644 --- a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C +++ b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C @@ -17,13 +17,14 @@ #include "GPUReconstruction.h" #include "GPUChainTracking.h" #include "GPUSettings.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTRDDef.h" #include "GPUTRDTrack.h" #include "GPUTRDTracker.h" #include "GPUTRDTrackletWord.h" #include "GPUTRDInterfaces.h" #include "GPUTRDGeometry.h" +#include "GPUTRDRecoParam.h" // O2 header #include "CommonUtils/NameConf.h" @@ -58,7 +59,7 @@ void run_trd_tracker(std::string path = "./", geo->createPadPlaneArray(); geo->createClusterMatrixArray(); const o2::trd::GeometryFlat geoFlat(*geo); - + o2::gpu::GPUTRDRecoParam trdRecoParam; //-------- init GPU reconstruction --------// // different settings are defined in GPUSettingsList.h GPUSettingsGRP cfgGRP; // defaults should be ok @@ -70,7 +71,7 @@ void run_trd_tracker(std::string path = "./", GPUSettingsProcessing cfgDeviceProcessing; // also keep defaults here, or adjust debug level cfgDeviceProcessing.debugLevel = 5; GPURecoStepConfiguration cfgRecoStep; - cfgRecoStep.steps = GPUDataTypes::RecoStep::NoRecoStep; + cfgRecoStep.steps = gpudatatypes::RecoStep::NoRecoStep; cfgRecoStep.inputs.clear(); cfgRecoStep.outputs.clear(); auto rec = GPUReconstruction::CreateInstance("CPU", true); @@ -85,6 +86,7 @@ void run_trd_tracker(std::string path = "./", rec->RegisterGPUProcessor(tracker, false); chainTracking->SetTRDGeometry(&geoFlat); + chainTracking->SetTRDRecoParam(&trdRecoParam); if (rec->Init()) { printf("ERROR: GPUReconstruction not initialized\n"); } diff --git a/GPU/GPUTracking/cmake/vulkan_display.cmake b/GPU/GPUTracking/cmake/vulkan_display.cmake index 7cbfd0328c323..7859742363755 100644 --- a/GPU/GPUTracking/cmake/vulkan_display.cmake +++ b/GPU/GPUTracking/cmake/vulkan_display.cmake @@ -27,7 +27,7 @@ function(add_glslc_shader TARGET SHADER) OUTPUT ${spirv-file} COMMAND ${Vulkan_GLSLC_EXECUTABLE} -o ${spirv-file} ${input-file-abs} -MD -MT ${spirv-file} -MF ${spirv-file}.d DEPENDS ${input-file-abs} - DEPFILE ${input-file-abs}.d + DEPFILE ${spirv-file}.d COMMENT "Compiling GLSL to SPIRV: ${SHADER}" VERBATIM ) diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.cxx b/GPU/GPUTracking/dEdx/GPUdEdx.cxx index 340463b9ec7f7..7df2cd90dab1f 100644 --- a/GPU/GPUTracking/dEdx/GPUdEdx.cxx +++ b/GPU/GPUTracking/dEdx/GPUdEdx.cxx @@ -19,6 +19,8 @@ using namespace o2::gpu; +static_assert(GPUdEdx::MAX_NCL <= 255); // Must fit in mNClsROC (uint8_t)! + #ifndef GPUCA_GPUCODE_DEVICE GPUd() void GPUdEdx::clear() { diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.h b/GPU/GPUTracking/dEdx/GPUdEdx.h index b711f1a685a4c..e64e2b37945c8 100644 --- a/GPU/GPUTracking/dEdx/GPUdEdx.h +++ b/GPU/GPUTracking/dEdx/GPUdEdx.h @@ -23,6 +23,7 @@ #include "CalibdEdxContainer.h" #include "GPUTPCGeometry.h" #include "GPUDebugStreamer.h" +#include "GPUDataTypesIO.h" namespace o2::gpu { @@ -36,6 +37,8 @@ class GPUdEdx GPUd() void fillSubThreshold(int32_t padRow); GPUd() void computedEdx(GPUdEdxInfo& output, const GPUParam& param); + static constexpr size_t MAX_NCL = GPUCA_ROW_COUNT; + private: GPUd() float GetSortTruncMean(GPUCA_PAR_DEDX_STORAGE_TYPE_A* array, int32_t count, int32_t trunclow, int32_t trunchigh); GPUd() void checkSubThresh(int32_t roc); @@ -60,8 +63,6 @@ class GPUdEdx }; #endif - static constexpr int32_t MAX_NCL = GPUCA_ROW_COUNT; // Must fit in mNClsROC (uint8_t)! - GPUCA_PAR_DEDX_STORAGE_TYPE_A mChargeTot[MAX_NCL]; // No need for default, just some memory GPUCA_PAR_DEDX_STORAGE_TYPE_A mChargeMax[MAX_NCL]; // No need for default, just some memory float mSubThreshMinTot = 0.f; @@ -76,7 +77,7 @@ class GPUdEdx GPUdi() void GPUdEdx::checkSubThresh(int32_t roc) { if (roc != mLastROC) { - if (mNSubThresh && mCount + mNSubThresh <= MAX_NCL) { + if (mNSubThresh && mCount + mNSubThresh < MAX_NCL) { for (int32_t i = 0; i < mNSubThresh; i++) { mChargeTot[mCount] = (GPUCA_PAR_DEDX_STORAGE_TYPE_A)(mSubThreshMinTot * scalingFactor::factor + scalingFactor::round); mChargeMax[mCount++] = (GPUCA_PAR_DEDX_STORAGE_TYPE_A)(mSubThreshMinMax * scalingFactor::factor + scalingFactor::round); @@ -94,16 +95,15 @@ GPUdi() void GPUdEdx::checkSubThresh(int32_t roc) GPUdnii() void GPUdEdx::fillCluster(float qtot, float qmax, int32_t padRow, uint8_t sector, float trackSnp, float trackTgl, const GPUCalibObjectsConst& calib, float z, float pad, float relTime) { - if (mCount >= MAX_NCL) { - return; - } - // container containing all the dE/dx corrections auto calibContainer = calib.dEdxCalibContainer; constexpr GPUTPCGeometry geo; const int32_t roc = geo.GetROC(padRow); checkSubThresh(roc); + if (mCount >= MAX_NCL) { + return; + } float snp2 = trackSnp * trackSnp; if (snp2 > GPUCA_MAX_SIN_PHI_LOW) { snp2 = GPUCA_MAX_SIN_PHI_LOW; diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index 32d25ee08b729..82ce0d4a9b190 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -151,14 +151,13 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") add_library(O2::${MODULE} ALIAS ${MODULE}) target_link_libraries(${targetName} PUBLIC O2::GPUTracking) install(TARGETS ${MODULE}) - - install(DIRECTORY filterMacros/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter FILES_MATCHING PATTERN "*.C") - get_property(GPU_DISPLAY_INCLUDE_PATH DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) - configure_file(filterMacros/setinclude.sh.in setinclude.sh @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/setinclude.sh PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter) endif() install(FILES ${HDRS} ${HDRS_INSTALL} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/GPU) +install(DIRECTORY filterMacros/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter FILES_MATCHING PATTERN "*.C") +get_property(GPU_DISPLAY_INCLUDE_PATH DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) +configure_file(filterMacros/setinclude.sh.in setinclude.sh @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/setinclude.sh PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter) target_compile_definitions(${targetName} PRIVATE $) diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 7279f2ee87fdb..b0c1c1d11f2cf 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -19,6 +19,7 @@ #include "backend/GPUDisplayBackend.h" #include "GPUDisplayInterface.h" #include "GPUSettings.h" +#include "GPUDefConstantsAndSettings.h" #include #include @@ -36,6 +37,7 @@ class GPUTPCTracker; struct GPUParam; class GPUQA; class GPUTRDGeometry; +class GPUTPCGMPropagator; class GPUDisplay : public GPUDisplayInterface { diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx index 0c2af55121b7d..a1bee6ce47ebd 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx @@ -481,8 +481,6 @@ void GPUDisplayBackendVulkan::createDevice() deviceCreateInfo.pEnabledFeatures = &deviceFeatures; deviceCreateInfo.enabledExtensionCount = static_cast(reqDeviceExtensions.size()); deviceCreateInfo.ppEnabledExtensionNames = reqDeviceExtensions.data(); - deviceCreateInfo.enabledLayerCount = instanceCreateInfo.enabledLayerCount; - deviceCreateInfo.ppEnabledLayerNames = instanceCreateInfo.ppEnabledLayerNames; mDevice = mPhysicalDevice.createDevice(deviceCreateInfo, nullptr); VULKAN_HPP_DEFAULT_DISPATCHER.init(mDevice); mGraphicsQueue = mDevice.getQueue(mGraphicsFamily, 0); diff --git a/GPU/GPUTracking/display/filterMacros/setinclude.sh.in b/GPU/GPUTracking/display/filterMacros/setinclude.sh.in index c588923db4b43..0a301537bba0e 100755 --- a/GPU/GPUTracking/display/filterMacros/setinclude.sh.in +++ b/GPU/GPUTracking/display/filterMacros/setinclude.sh.in @@ -1,2 +1,2 @@ #!/bin/bash -export ROOC_INCLUDE_PATH="@GPU_DISPLAY_INCLUDE_PATH@" +export ROOT_INCLUDE_PATH="@GPU_DISPLAY_INCLUDE_PATH@" diff --git a/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx b/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx index 4953815a6fc19..6447d30daefe3 100644 --- a/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx +++ b/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx @@ -817,7 +817,7 @@ size_t GPUDisplay::DrawGLScene_updateVertexList() int32_t numThreads = getNumThreads(); tbb::task_arena(numThreads).execute([&] { - if (mChain && (mChain->GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking)) { + if (mChain && (mChain->GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking)) { tbb::parallel_for(0, NSECTORS, [&](int32_t iSector) { GPUTPCTracker& tracker = (GPUTPCTracker&)sectorTracker(iSector); tracker.SetPointersDataLinks(tracker.LinkTmpMemory()); @@ -964,7 +964,6 @@ size_t GPUDisplay::DrawGLScene_updateVertexList() if (timer.IsRunning()) { GPUInfo("Display Time: Vertex Clusters:\t\t\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); } - }); // End omp parallel diff --git a/GPU/GPUTracking/kernels.cmake b/GPU/GPUTracking/kernels.cmake index c8ddcd2e9d81d..4d1abe9f5b064 100644 --- a/GPU/GPUTracking/kernels.cmake +++ b/GPU/GPUTracking/kernels.cmake @@ -118,9 +118,9 @@ o2_gpu_add_kernel("GPUTPCCFStreamCompaction, scanUp" "= TPC o2_gpu_add_kernel("GPUTPCCFStreamCompaction, scanTop" "= TPCCLUSTERFINDER" LB int32_t iBuf int32_t nElems) o2_gpu_add_kernel("GPUTPCCFStreamCompaction, scanDown" "= TPCCLUSTERFINDER" LB int32_t iBuf uint32_t offset int32_t nElems) o2_gpu_add_kernel("GPUTPCCFStreamCompaction, compactDigits" "= TPCCLUSTERFINDER" LB int32_t iBuf int32_t stage CfChargePos* in CfChargePos* out) -o2_gpu_add_kernel("GPUTPCCFDecodeZS" "= TPCCLUSTERFINDER" LB int32_t firstHBF) -o2_gpu_add_kernel("GPUTPCCFDecodeZSLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) -o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) +o2_gpu_add_kernel("GPUTPCCFDecodeZS" "= TPCCLUSTERFINDER" LB int32_t firstHBF int32_t tpcTimeBinCut) +o2_gpu_add_kernel("GPUTPCCFDecodeZSLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF int32_t tpcTimeBinCut) +o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS ERRORS" LB int32_t firstHBF int32_t tpcTimeBinCut) o2_gpu_add_kernel("GPUTPCCFGather" "=" LB o2::tpc::ClusterNative* dest) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode0asGPU" "= GLOBALREFIT " LB) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode1asTrackParCov" "= GLOBALREFIT " LB) @@ -145,7 +145,8 @@ o2_gpu_kernel_add_parameter(NEIGHBOURS_FINDER_MAX_NNEIGHUP # Number of neighhbo COMP_GATHER_KERNEL # Default kernel to use for Compression Gather Operation [0 - 4] COMP_GATHER_MODE # TPC Compression Gather Mode [0 - 3] SORT_STARTHITS # Sort start hits to improve cache locality during tracklet construction [0/1] - CF_SCAN_WORKGROUP_SIZE) # Work group size to use in clusterizer scan operation + CF_SCAN_WORKGROUP_SIZE # Work group size to use in clusterizer scan operation + AMD_EUS_PER_CU) # Number of SIMD units per Compute Unit (only for AMD GPUs) o2_gpu_kernel_add_string_parameter(DEDX_STORAGE_TYPE # Data type to use for intermediate storage of dEdx truncated mean inputs MERGER_INTERPOLATION_ERROR_TYPE) # Data type for storing intermediate track residuals for interpolation diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 4f2c13635befa..b58209efff744 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -55,6 +55,7 @@ #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "GPUSettings.h" +#include "GPUDefMacros.h" #ifdef GPUCA_O2_LIB #include "DetectorsRaw/HBFUtils.h" #include "DataFormatsTPC/TrackTPC.h" @@ -73,6 +74,7 @@ #include #include #include +#include #include "utils/timer.h" @@ -80,58 +82,55 @@ using namespace o2::gpu; -#ifdef GPUCA_MERGER_BY_MC_LABEL -#define CHECK_CLUSTER_STATE_INIT_LEG_BY_MC() \ - if (!unattached && mTrackMCLabels[id].isValid()) { \ - int32_t mcLabel = mTrackMCLabels[id].getTrackID(); \ - int32_t mcEvent = mTrackMCLabels[id].getEventID(); \ - int32_t mcSource = mTrackMCLabels[id].getSourceID(); \ - if (mTrackMCLabelsReverse[mMCEventOffset[mcSource] + mcEvent][mcLabel] != id) { \ - attach &= (~gputpcgmmergertypes::attachGoodLeg); \ - } \ - } -#else -#define CHECK_CLUSTER_STATE_INIT_LEG_BY_MC() -#endif +namespace o2::gpu +{ +struct checkClusterStateResult { + bool unattached = false; + float qpt = 0.f; + bool lowPt = false; + bool mev200 = false; + bool mergedLooperUnconnected = false; + bool mergedLooperConnected = false; + int32_t id = 0; + bool physics = false, protect = false; +}; +} // namespace o2::gpu -#define CHECK_CLUSTER_STATE_INIT() \ - bool unattached = attach == 0; \ - float qpt = 0; \ - bool lowPt = false; \ - [[maybe_unused]] bool mev200 = false; \ - bool mergedLooperUnconnected = false; \ - bool mergedLooperConnected = false; \ - int32_t id = attach & gputpcgmmergertypes::attachTrackMask; \ - if (!unattached) { \ - qpt = fabsf(mTracking->mIOPtrs.mergedTracks[id].GetParam().GetQPt()); \ - lowPt = qpt * mTracking->GetParam().qptB5Scaler > mTracking->GetParam().rec.tpc.rejectQPtB5; \ - mev200 = qpt > 5; \ - mergedLooperUnconnected = mTracking->mIOPtrs.mergedTracks[id].MergedLooperUnconnected(); \ - mergedLooperConnected = mTracking->mIOPtrs.mergedTracks[id].MergedLooperConnected(); \ - } \ - bool physics = false, protect = false; \ - CHECK_CLUSTER_STATE_INIT_LEG_BY_MC(); - -#define CHECK_CLUSTER_STATE() \ - CHECK_CLUSTER_STATE_INIT() \ - if (mev200) { \ - mClusterCounts.n200MeV++; \ - } \ - if (lowPt) { \ - mClusterCounts.nLowPt++; \ - } else if (mergedLooperUnconnected) { \ - mClusterCounts.nMergedLooperUnconnected++; \ - } else if (mergedLooperConnected) { \ - mClusterCounts.nMergedLooperConnected++; \ - } else { \ - GPUTPCClusterRejection::GetProtectionStatus(attach, physics, protect, &mClusterCounts, &mev200); \ - } - -#define CHECK_CLUSTER_STATE_NOCOUNT() \ - CHECK_CLUSTER_STATE_INIT() \ - if (!lowPt && !mergedLooperUnconnected && !mergedLooperConnected) { \ - GPUTPCClusterRejection::GetProtectionStatus(attach, physics, protect); \ +template +inline checkClusterStateResult GPUQA::checkClusterState(uint32_t attach, T* counts) const +{ + checkClusterStateResult r; + r.unattached = attach == 0; + r.id = attach & gputpcgmmergertypes::attachTrackMask; + if (!r.unattached && !(attach & gputpcgmmergertypes::attachProtect)) { + r.qpt = fabsf(mTracking->mIOPtrs.mergedTracks[r.id].GetParam().GetQPt()); + r.lowPt = r.qpt * mTracking->GetParam().qptB5Scaler > mTracking->GetParam().rec.tpc.rejectQPtB5; + r.mev200 = r.qpt > 5; + r.mergedLooperUnconnected = mTracking->mIOPtrs.mergedTracks[r.id].MergedLooperUnconnected(); + r.mergedLooperConnected = mTracking->mIOPtrs.mergedTracks[r.id].MergedLooperConnected(); + } + if (r.mev200) { + if constexpr (COUNT) { + counts->n200MeV++; + } } + if (r.lowPt) { + if constexpr (COUNT) { + counts->nLowPt++; + } + } else if (r.mergedLooperUnconnected) { + if constexpr (COUNT) { + counts->nMergedLooperUnconnected++; + } + } else if (r.mergedLooperConnected) { + if constexpr (COUNT) { + counts->nMergedLooperConnected++; + } + } else if (attach) { + r.protect = !GPUTPCClusterRejection::GetRejectionStatus(attach, r.physics, counts, &r.mev200) && ((attach & gputpcgmmergertypes::attachProtect) || !GPUTPCClusterRejection::IsTrackRejected(mTracking->mIOPtrs.mergedTracks[r.id], mTracking->GetParam())); + } + return r; +} static const GPUSettingsQA& GPUQA_GetConfig(GPUChainTracking* chain) { @@ -143,28 +142,23 @@ static const GPUSettingsQA& GPUQA_GetConfig(GPUChainTracking* chain) } } -// static const constexpr bool PLOT_ROOT = 0; -// static const constexpr bool FIX_SCALES = 0; -static const constexpr bool PERF_FIGURE = 0; -// static const constexpr float FIXED_SCALES_MIN[5] = {-0.05, -0.05, -0.2, -0.2, -0.5}; -// static const constexpr float FIXED_SCALES_MAX[5] = {0.4, 0.7, 5, 3, 6.5}; static const constexpr float LOG_PT_MIN = -1.; static constexpr float Y_MAX = 40; static constexpr float Z_MAX = 100; -static constexpr float PT_MIN = GPUCA_MIN_TRACK_PTB5_DEFAULT; -// static constexpr float PT_MIN2 = 0.1; +static constexpr float PT_MIN = 0.01; // TODO: Take from Param static constexpr float PT_MIN_PRIM = 0.1; -static constexpr float PT_MIN_CLUST = GPUCA_MIN_TRACK_PTB5_DEFAULT; +static constexpr float PT_MIN_CLUST = 0.01; static constexpr float PT_MAX = 20; static constexpr float ETA_MAX = 1.5; static constexpr float ETA_MAX2 = 0.9; +static constexpr int32_t PADROW_CHECK_MINCLS = 50; static constexpr bool CLUST_HIST_INT_SUM = false; static constexpr const int32_t COLORCOUNT = 12; -static const constexpr char* EFF_TYPES[5] = {"Rec", "Clone", "Fake", "All", "RecAndClone"}; +static const constexpr char* EFF_TYPES[6] = {"Rec", "Clone", "Fake", "All", "RecAndClone", "MC"}; static const constexpr char* FINDABLE_NAMES[2] = {"All", "Findable"}; static const constexpr char* PRIM_NAMES[2] = {"Prim", "Sec"}; static const constexpr char* PARAMETER_NAMES[5] = {"Y", "Z", "#Phi", "#lambda", "Relative #it{p}_{T}"}; @@ -183,6 +177,7 @@ static const constexpr char* CLUSTER_NAMES[GPUQA::N_CLS_HIST] = {"Correctly atta static const constexpr char* CLUSTER_TITLES[GPUQA::N_CLS_TYPE] = {"Clusters Pt Distribution / Attachment", "Clusters Pt Distribution / Attachment (relative to all clusters)", "Clusters Pt Distribution / Attachment (integrated)"}; static const constexpr char* CLUSTER_NAMES_SHORT[GPUQA::N_CLS_HIST] = {"Attached", "Fake", "AttachAdjacent", "FakeAdjacent", "FoundTracks", "Physics", "Protected", "All"}; static const constexpr char* CLUSTER_TYPES[GPUQA::N_CLS_TYPE] = {"", "Ratio", "Integral"}; +static const constexpr char* REJECTED_NAMES[3] = {"All", "Rejected", "Fraction"}; static const constexpr int32_t COLORS_HEX[COLORCOUNT] = {0xB03030, 0x00A000, 0x0000C0, 0x9400D3, 0x19BBBF, 0xF25900, 0x7F7F7F, 0xFFD700, 0x07F707, 0x07F7F7, 0xF08080, 0x000000}; static const constexpr int32_t CONFIG_DASHED_MARKERS = 0; @@ -355,11 +350,11 @@ GPUQA::~GPUQA() bool GPUQA::clusterRemovable(int32_t attach, bool prot) const { - CHECK_CLUSTER_STATE_NOCOUNT(); + const auto& r = checkClusterState(attach); if (prot) { - return protect || physics; + return r.protect || r.physics; } - return (!unattached && !physics && !protect); + return (!r.unattached && !r.physics && !r.protect); } template @@ -374,10 +369,10 @@ void GPUQA::SetAxisSize(T* e) e->GetXaxis()->SetLabelSize(0.045); } -void GPUQA::SetLegend(TLegend* l) +void GPUQA::SetLegend(TLegend* l, bool bigText) { l->SetTextFont(72); - l->SetTextSize(0.016); + l->SetTextSize(bigText ? 0.03 : 0.016); l->SetFillColor(0); } @@ -418,14 +413,20 @@ void GPUQA::DrawHisto(TH1* histo, char* filename, char* options) void GPUQA::doPerfFigure(float x, float y, float size) { - if (!PERF_FIGURE) { + if (mConfig.perfFigure == "") { return; } - TLatex* t = createGarbageCollected(); + static constexpr const char* str_perf_figure_1 = "ALICE Performance"; + static constexpr const char* str_perf_figure_2_mc = "MC, Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; + static constexpr const char* str_perf_figure_2_data = "Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; + const char* str_perf_figure_2 = (mConfig.perfFigure == "mc" || mConfig.perfFigure == "MC") ? str_perf_figure_2_mc : (mConfig.perfFigure == "data" ? str_perf_figure_2_data : mConfig.perfFigure.c_str()); + + TLatex* t = createGarbageCollected(); // TODO: We could perhaps put everything in a legend, to get a white background if there is a grid t->SetNDC(kTRUE); t->SetTextColor(1); t->SetTextSize(size); t->DrawLatex(x, y, str_perf_figure_1); + t->SetTextSize(size * 0.8); t->DrawLatex(x, y - 0.01 - size, str_perf_figure_2); } @@ -440,7 +441,7 @@ int32_t GPUQA::InitQACreateHistograms() char name[2048], fname[1024]; if (mQATasks & taskTrackingEff) { // Create Efficiency Histograms - for (int32_t i = 0; i < 5; i++) { + for (int32_t i = 0; i < 6; i++) { for (int32_t j = 0; j < 2; j++) { for (int32_t k = 0; k < 2; k++) { for (int32_t l = 0; l < 5; l++) { @@ -525,6 +526,11 @@ int32_t GPUQA::InitQACreateHistograms() std::unique_ptr binsPt{CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX)}; createHist(mClusters[i], name, name, AXIS_BINS[4], binsPt.get()); } + + createHist(mPadRow[0], "padrow0", "padrow0", GPUCA_ROW_COUNT - PADROW_CHECK_MINCLS, 0, GPUCA_ROW_COUNT - 1 - PADROW_CHECK_MINCLS, GPUCA_ROW_COUNT - PADROW_CHECK_MINCLS, 0, GPUCA_ROW_COUNT - 1 - PADROW_CHECK_MINCLS); + createHist(mPadRow[1], "padrow1", "padrow1", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT - PADROW_CHECK_MINCLS, 0, GPUCA_ROW_COUNT - 1 - PADROW_CHECK_MINCLS); + createHist(mPadRow[2], "padrow2", "padrow2", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT - PADROW_CHECK_MINCLS, 0, GPUCA_ROW_COUNT - 1 - PADROW_CHECK_MINCLS); + createHist(mPadRow[3], "padrow3", "padrow3", 100.f, 0, 300000, GPUCA_ROW_COUNT - PADROW_CHECK_MINCLS, 0, GPUCA_ROW_COUNT - 1 - PADROW_CHECK_MINCLS); } if (mQATasks & taskTrackStatistics) { @@ -534,12 +540,20 @@ int32_t GPUQA::InitQACreateHistograms() createHist(mNCl[i], name, name, 160, 0, 159); } std::unique_ptr binsPt{CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX)}; - createHist(mTracks, "tracks_pt", "tracks_pt", AXIS_BINS[4], binsPt.get()); + createHist(mTrackPt, "tracks_pt", "tracks_pt", AXIS_BINS[4], binsPt.get()); const uint32_t maxTime = (mTracking && mTracking->GetParam().continuousMaxTimeBin > 0) ? mTracking->GetParam().continuousMaxTimeBin : TPC_MAX_TIME_BIN_TRIGGERED; createHist(mT0[0], "tracks_t0", "tracks_t0", (maxTime + 1) / 10, 0, maxTime); createHist(mT0[1], "tracks_t0_res", "tracks_t0_res", 1000, -100, 100); createHist(mClXY, "clXY", "clXY", 1000, -250, 250, 1000, -250, 250); // TODO: Pass name only once } + if (mQATasks & taskClusterRejection) { + const int padCount = GPUTPCGeometry::NPads(GPUCA_ROW_COUNT - 1); + for (int32_t i = 0; i < 3; i++) { + snprintf(name, 2048, "clrej_%d", i); + createHist(mClRej[i], name, name, 2 * padCount, -padCount / 2 + 0.5f, padCount / 2 - 0.5f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); + } + createHist(mClRejP, "clrejp", "clrejp", GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); + } if ((mQATasks & taskClusterCounts) && mConfig.clusterRejectionHistograms) { int32_t num = DoClusterCounts(nullptr, 2); @@ -565,8 +579,8 @@ int32_t GPUQA::InitQACreateHistograms() int32_t GPUQA::loadHistograms(std::vector& i1, std::vector& i2, std::vector& i3, std::vector& i4, int32_t tasks) { - if (tasks == -1) { - tasks = taskDefaultPostprocess; + if (tasks == tasksAutomatic) { + tasks = tasksDefaultPostprocess; } if (mQAInitialized && (!mHaveExternalHists || tasks != mQATasks)) { throw std::runtime_error("QA not initialized or initialized with different task array"); @@ -581,7 +595,7 @@ int32_t GPUQA::loadHistograms(std::vector& i1, std::vector& i2, std: mHistGraph_pos.clear(); mHaveExternalHists = true; if (mConfig.noMC) { - tasks &= tasksNoQC; + tasks &= tasksAllNoQC; } mQATasks = tasks; if (InitQACreateHistograms()) { @@ -794,8 +808,8 @@ int32_t GPUQA::InitQA(int32_t tasks) if (mQAInitialized) { throw std::runtime_error("QA already initialized"); } - if (tasks == -1) { - tasks = taskDefault; + if (tasks == tasksAutomatic) { + tasks = tasksDefault; } mHist1D = new std::vector; @@ -803,7 +817,7 @@ int32_t GPUQA::InitQA(int32_t tasks) mHist1Dd = new std::vector; mHistGraph = new std::vector; if (mConfig.noMC) { - tasks &= tasksNoQC; + tasks &= tasksAllNoQC; } mQATasks = tasks; @@ -820,7 +834,7 @@ int32_t GPUQA::InitQA(int32_t tasks) } if (mConfig.enableLocalOutput) { - mkdir("plots", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + mkdir(mConfig.plotsDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); } #ifdef GPUCA_O2_LIB @@ -958,7 +972,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx nClusters++; uint32_t hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; if (hitId >= GetNMCLabels()) { - GPUError("Invalid hit id %u > %d (nClusters %d)", hitId, GetNMCLabels(), mTracking->mIOPtrs.clustersNative ? mTracking->mIOPtrs.clustersNative->nClustersTotal : 0); + GPUError("Invalid hit id %u > %d (nClusters %d)", hitId, GetNMCLabels(), clNative ? clNative->nClustersTotal : 0); throw std::runtime_error("qa error"); } acc.addLabel(hitId); @@ -1059,26 +1073,65 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } } - if ((mQATasks & taskClusterAttach) && mTracking->mIOPtrs.mergedTrackHitAttachment) { + if ((mQATasks & taskClusterAttach) && !tracksExternal) { + std::vector lowestPadRow(mTracking->mIOPtrs.nMergedTracks); // fill cluster adjacent status - for (uint32_t i = 0; i < GetNMCLabels(); i++) { - if (mClusterParam[i].attached == 0 && mClusterParam[i].fakeAttached == 0) { - int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; - if (attach & gputpcgmmergertypes::attachFlagMask) { - int32_t track = attach & gputpcgmmergertypes::attachTrackMask; - mcLabelI_t trackL = mTrackMCLabels[track]; - bool fake = true; - for (int32_t j = 0; j < GetMCLabelNID(i); j++) { - // GPUInfo("Attach %x Track %d / %d:%d", attach, track, j, GetMCLabelID(i, j)); - if (trackL == GetMCLabel(i, j)) { - fake = false; - break; + if (mTracking->mIOPtrs.mergedTrackHitAttachment) { + for (uint32_t i = 0; i < GetNMCLabels(); i++) { + if (mClusterParam[i].attached == 0 && mClusterParam[i].fakeAttached == 0) { + int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; + if (attach & gputpcgmmergertypes::attachFlagMask) { + int32_t track = attach & gputpcgmmergertypes::attachTrackMask; + mcLabelI_t trackL = mTrackMCLabels[track]; + bool fake = true; + for (int32_t j = 0; j < GetMCLabelNID(i); j++) { + // GPUInfo("Attach %x Track %d / %d:%d", attach, track, j, GetMCLabelID(i, j)); + if (trackL == GetMCLabel(i, j)) { + fake = false; + break; + } + } + if (fake) { + mClusterParam[i].fakeAdjacent++; + } else { + mClusterParam[i].adjacent++; } } - if (fake) { - mClusterParam[i].fakeAdjacent++; - } else { - mClusterParam[i].adjacent++; + } + } + } + if (mTracking->mIOPtrs.nMergedTracks && clNative) { + std::fill(lowestPadRow.begin(), lowestPadRow.end(), 255); + for (uint32_t iSector = 0; iSector < GPUCA_NSECTORS; iSector++) { + for (uint32_t iRow = 0; iRow < GPUCA_ROW_COUNT; iRow++) { + for (uint32_t iCl = 0; iCl < clNative->nClusters[iSector][iRow]; iCl++) { + int32_t i = clNative->clusterOffset[iSector][iRow] + iCl; + for (int32_t j = 0; j < GetMCLabelNID(i); j++) { + uint32_t trackId = GetMCTrackObj(mTrackMCLabelsReverse, GetMCLabel(i, j)); + if (trackId < lowestPadRow.size() && lowestPadRow[trackId] > iRow) { + lowestPadRow[trackId] = iRow; + } + } + } + } + } + for (uint32_t i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { + const auto& trk = mTracking->mIOPtrs.mergedTracks[i]; + if (trk.OK() && lowestPadRow[i] != 255 && trk.NClustersFitted() >= PADROW_CHECK_MINCLS && CAMath::Abs(trk.GetParam().GetQPt()) < 1.0) { + const auto& lowestCl = mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef()].row < mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef() + trk.NClusters() - 1].row ? mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef()] : mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef() + trk.NClusters() - 1]; + const int32_t lowestRow = lowestCl.row; + mPadRow[0]->Fill(lowestPadRow[i], lowestRow, 1.f); + mPadRow[1]->Fill(CAMath::ATan2(trk.GetParam().GetY(), trk.GetParam().GetX()), lowestRow, 1.f); + if (lowestPadRow[i] < 10 && lowestRow > lowestPadRow[i] + 3) { + const auto& cl = clNative->clustersLinear[lowestCl.num]; + float x, y, z; + mTracking->GetTPCTransformHelper()->Transform(lowestCl.sector, lowestCl.row, cl.getPad(), cl.getTime(), x, y, z, trk.GetParam().GetTOffset()); + float phi = CAMath::ATan2(y, x); + mPadRow[2]->Fill(phi, lowestRow, 1.f); + if (CAMath::Abs(phi) < 0.15) { + const float time = cl.getTime(); + mPadRow[3]->Fill(mTracking->GetParam().GetUnscaledMult(time), lowestRow, 1.f); + } } } } @@ -1236,7 +1289,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx effdump.Fill(alpha, localX, localY, info.z, mcphi, mceta, mcpt, mRecTracks[iCol][i], mFakeTracks[iCol][i], findable, info.prim, mc2.nWeightCls); } - for (int32_t j = 0; j < 4; j++) { + for (int32_t j = 0; j < 6; j++) { + if (j == 3 || j == 4) { + continue; + } for (int32_t k = 0; k < 2; k++) { if (k == 0 && findable == 0) { continue; @@ -1442,7 +1498,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } - if (mQATasks & taskClusterAttach) { + if ((mQATasks & taskClusterAttach) && !tracksExternal) { // Fill cluster histograms for (uint32_t iTrk = 0; iTrk < nReconstructedTracks; iTrk++) { const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[iTrk]; @@ -1457,17 +1513,17 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx int32_t hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; float totalWeight = 0.; for (int32_t j = 0; j < GetMCLabelNID(hitId); j++) { - if (GetMCLabelID(hitId, j) >= 0 && GetMCTrackObj(mMCParam, GetMCLabel(hitId, j)).pt > GPUCA_MIN_TRACK_PTB5_DEFAULT) { + if (GetMCLabelID(hitId, j) >= 0 && GetMCTrackObj(mMCParam, GetMCLabel(hitId, j)).pt > 1.f / mTracking->GetParam().rec.maxTrackQPtB5) { totalWeight += GetMCLabelWeight(hitId, j); } } int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[hitId]; - CHECK_CLUSTER_STATE_NOCOUNT(); + const auto& r = checkClusterState(attach); if (totalWeight > 0) { float weight = 1.f / (totalWeight * (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached)); for (int32_t j = 0; j < GetMCLabelNID(hitId); j++) { mcLabelI_t label = GetMCLabel(hitId, j); - if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > GPUCA_MIN_TRACK_PTB5_DEFAULT) { + if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > 1.f / mTracking->GetParam().rec.maxTrackQPtB5) { float pt = GetMCTrackObj(mMCParam, label).pt; if (pt < PT_MIN_CLUST) { pt = PT_MIN_CLUST; @@ -1478,10 +1534,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_tracks]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); } mClusters[CL_all]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); } } @@ -1492,10 +1548,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_att_adj]->Fill(0.f, weight); mClusters[CL_all]->Fill(0.f, weight); mClusterCounts.nUnaccessible += weight; - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(0.f, weight); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(0.f, weight); } } @@ -1532,11 +1588,11 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_att_adj]->Fill(pt, weight); mClusters[CL_all]->Fill(pt, weight); int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[hitId]; - CHECK_CLUSTER_STATE_NOCOUNT(); - if (protect || physics) { + const auto& r = checkClusterState(attach); + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(pt, weight); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(pt, weight); } } @@ -1549,14 +1605,14 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx continue; } int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; - CHECK_CLUSTER_STATE_NOCOUNT(); + const auto& r = checkClusterState(attach); if (mClusterParam[i].adjacent) { int32_t label = mTracking->mIOPtrs.mergedTrackHitAttachment[i] & gputpcgmmergertypes::attachTrackMask; if (!mTrackMCLabels[label].isValid()) { float totalWeight = 0.; for (int32_t j = 0; j < GetMCLabelNID(i); j++) { mcLabelI_t labelT = GetMCLabel(i, j); - if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PTB5_DEFAULT) { + if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > 1.f / mTracking->GetParam().rec.maxTrackQPtB5) { totalWeight += GetMCLabelWeight(i, j); } } @@ -1564,7 +1620,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx if (totalWeight > 0) { for (int32_t j = 0; j < GetMCLabelNID(i); j++) { mcLabelI_t labelT = GetMCLabel(i, j); - if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PTB5_DEFAULT) { + if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > 1.f / mTracking->GetParam().rec.maxTrackQPtB5) { float pt = GetMCTrackObj(mMCParam, labelT).pt; if (pt < PT_MIN_CLUST) { pt = PT_MIN_CLUST; @@ -1575,10 +1631,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_att_adj]->Fill(pt, GetMCLabelWeight(i, j) * weight); mClusters[CL_fakeAdj]->Fill(pt, GetMCLabelWeight(i, j) * weight); mClusters[CL_all]->Fill(pt, GetMCLabelWeight(i, j) * weight); - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(pt, GetMCLabelWeight(i, j) * weight); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(pt, GetMCLabelWeight(i, j) * weight); } } @@ -1588,10 +1644,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_fakeAdj]->Fill(0.f, 1.f); mClusters[CL_all]->Fill(0.f, 1.f); mClusterCounts.nUnaccessible++; - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(0.f, 1.f); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(0.f, 1.f); } } @@ -1603,10 +1659,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_att_adj]->Fill(pt, 1.f); mClusters[CL_tracks]->Fill(pt, 1.f); mClusters[CL_all]->Fill(pt, 1.f); - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(pt, 1.f); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(pt, 1.f); } } @@ -1614,14 +1670,14 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx float totalWeight = 0.; for (int32_t j = 0; j < GetMCLabelNID(i); j++) { mcLabelI_t labelT = GetMCLabel(i, j); - if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PTB5_DEFAULT) { + if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > 1.f / mTracking->GetParam().rec.maxTrackQPtB5) { totalWeight += GetMCLabelWeight(i, j); } } if (totalWeight > 0) { for (int32_t j = 0; j < GetMCLabelNID(i); j++) { mcLabelI_t label = GetMCLabel(i, j); - if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > GPUCA_MIN_TRACK_PTB5_DEFAULT) { + if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > 1.f / mTracking->GetParam().rec.maxTrackQPtB5) { float pt = GetMCTrackObj(mMCParam, label).pt; if (pt < PT_MIN_CLUST) { pt = PT_MIN_CLUST; @@ -1637,10 +1693,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx mClusters[CL_tracks]->Fill(pt, weight); } mClusters[CL_all]->Fill(pt, weight); - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(pt, weight); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(pt, weight); } } @@ -1654,10 +1710,10 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } mClusters[CL_all]->Fill(0.f, 1.f); mClusterCounts.nUnaccessible++; - if (protect || physics) { + if (r.protect || r.physics) { mClusters[CL_prot]->Fill(0.f, 1.f); } - if (physics) { + if (r.physics) { mClusters[CL_physics]->Fill(0.f, 1.f); } } @@ -1670,9 +1726,9 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } else if (!mConfig.inputHistogramsOnly && !mConfig.noMC && (mQATasks & (taskTrackingEff | taskTrackingRes | taskTrackingResPull | taskClusterAttach))) { GPUWarning("No MC information available, only running partial TPC QA!"); - } + } // mcAvail - if (mQATasks & taskTrackStatistics) { + if ((mQATasks & taskTrackStatistics) && !tracksExternal) { // Fill track statistic histograms std::vector> clusterAttachCounts; if (mcAvail) { @@ -1683,7 +1739,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx if (!track.OK()) { continue; } - mTracks->Fill(1.f / fabsf(track.GetParam().GetQPt())); + mTrackPt->Fill(1.f / fabsf(track.GetParam().GetQPt())); mNCl[0]->Fill(track.NClustersFitted()); uint32_t nClCorrected = 0; const auto& trackClusters = mTracking->mIOPtrs.mergedTrackHits; @@ -1725,7 +1781,9 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } } - mNCl[1]->Fill(nClCorrected); + if (nClCorrected) { + mNCl[1]->Fill(nClCorrected); + } mT0[0]->Fill(track.GetParam().GetTOffset()); if (mTrackMCLabels.size() && !mTrackMCLabels[i].isFake() && !track.MergedLooper() && !track.CCE()) { const auto& info = GetMCTrack(mTrackMCLabels[i]); @@ -1767,63 +1825,80 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx uint32_t nCl = clNative ? clNative->nClustersTotal : mTracking->GetProcessors()->tpcMerger.NMaxClusters(); mClusterCounts.nTotal += nCl; - if (mQATasks & taskClusterCounts) { - for (uint32_t i = 0; i < nCl; i++) { - int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; - CHECK_CLUSTER_STATE(); + if (mQATasks & (taskClusterCounts | taskClusterRejection)) { + for (uint32_t iSector = 0; iSector < GPUCA_NSECTORS; iSector++) { + for (uint32_t iRow = 0; iRow < GPUCA_ROW_COUNT; iRow++) { + for (uint32_t iCl = 0; iCl < clNative->nClusters[iSector][iRow]; iCl++) { + uint32_t i = clNative->clusterOffset[iSector][iRow] + iCl; + int32_t attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; + const auto& r = checkClusterState(attach, &mClusterCounts); - if (mcAvail) { - float totalWeight = 0, weight400 = 0, weight40 = 0; - for (int32_t j = 0; j < GetMCLabelNID(i); j++) { - const auto& label = GetMCLabel(i, j); - if (GetMCLabelID(label) >= 0) { - totalWeight += GetMCLabelWeight(label); - if (GetMCTrackObj(mMCParam, label).pt >= 0.4) { - weight400 += GetMCLabelWeight(label); + if (mQATasks & taskClusterRejection) { + if (mcAvail) { + float totalWeight = 0, weight400 = 0, weight40 = 0; + for (int32_t j = 0; j < GetMCLabelNID(i); j++) { + const auto& label = GetMCLabel(i, j); + if (GetMCLabelID(label) >= 0) { + totalWeight += GetMCLabelWeight(label); + if (GetMCTrackObj(mMCParam, label).pt >= 0.4) { + weight400 += GetMCLabelWeight(label); + } + if (GetMCTrackObj(mMCParam, label).pt <= 0.04) { + weight40 += GetMCLabelWeight(label); + } + } + } + if (totalWeight > 0 && 10.f * weight400 >= totalWeight) { + if (!r.unattached && !r.protect && !r.physics) { + mClusterCounts.nFakeRemove400++; + int32_t totalFake = weight400 < 0.9f * totalWeight; + if (totalFake) { + mClusterCounts.nFullFakeRemove400++; + } + /*printf("Fake removal (%d): Hit %7d, attached %d lowPt %d looper %d tube200 %d highIncl %d tube %d bad %d recPt %7.2f recLabel %6d", totalFake, i, (int32_t) (mClusterParam[i].attached || mClusterParam[i].fakeAttached), + (int32_t) lowPt, (int32_t) ((attach & gputpcgmmergertypes::attachGoodLeg) == 0), (int32_t) ((attach & gputpcgmmergertypes::attachTube) && mev200), + (int32_t) ((attach & gputpcgmmergertypes::attachHighIncl) != 0), (int32_t) ((attach & gputpcgmmergertypes::attachTube) != 0), (int32_t) ((attach & gputpcgmmergertypes::attachGood) == 0), + fabsf(qpt) > 0 ? 1.f / qpt : 0.f, id); + for (int32_t j = 0;j < GetMCLabelNID(i);j++) + { + //if (GetMCLabelID(i, j) < 0) break; + printf(" - label%d %6d weight %5d", j, GetMCLabelID(i, j), (int32_t) GetMCLabelWeight(i, j)); + if (GetMCLabelID(i, j) >= 0) printf(" - pt %7.2f", mMCParam[GetMCLabelID(i, j)].pt); + else printf(" "); + } + printf("\n");*/ + } + mClusterCounts.nAbove400++; + } + if (totalWeight > 0 && weight40 >= 0.9 * totalWeight) { + mClusterCounts.nBelow40++; + if (r.protect || r.physics) { + mClusterCounts.nFakeProtect40++; + } + } } - if (GetMCTrackObj(mMCParam, label).pt <= 0.04) { - weight40 += GetMCLabelWeight(label); + + if (r.physics) { + mClusterCounts.nPhysics++; } - } - } - if (totalWeight > 0 && 10.f * weight400 >= totalWeight) { - if (!unattached && !protect && !physics) { - mClusterCounts.nFakeRemove400++; - int32_t totalFake = weight400 < 0.9f * totalWeight; - if (totalFake) { - mClusterCounts.nFullFakeRemove400++; + if (r.protect) { + mClusterCounts.nProt++; } - /*printf("Fake removal (%d): Hit %7d, attached %d lowPt %d looper %d tube200 %d highIncl %d tube %d bad %d recPt %7.2f recLabel %6d", totalFake, i, (int32_t) (mClusterParam[i].attached || mClusterParam[i].fakeAttached), - (int32_t) lowPt, (int32_t) ((attach & gputpcgmmergertypes::attachGoodLeg) == 0), (int32_t) ((attach & gputpcgmmergertypes::attachTube) && mev200), - (int32_t) ((attach & gputpcgmmergertypes::attachHighIncl) != 0), (int32_t) ((attach & gputpcgmmergertypes::attachTube) != 0), (int32_t) ((attach & gputpcgmmergertypes::attachGood) == 0), - fabsf(qpt) > 0 ? 1.f / qpt : 0.f, id); - for (int32_t j = 0;j < GetMCLabelNID(i);j++) - { - //if (GetMCLabelID(i, j) < 0) break; - printf(" - label%d %6d weight %5d", j, GetMCLabelID(i, j), (int32_t) GetMCLabelWeight(i, j)); - if (GetMCLabelID(i, j) >= 0) printf(" - pt %7.2f", mMCParam[GetMCLabelID(i, j)].pt); - else printf(" "); + if (r.unattached) { + mClusterCounts.nUnattached++; } - printf("\n");*/ } - mClusterCounts.nAbove400++; - } - if (totalWeight > 0 && weight40 >= 0.9 * totalWeight) { - mClusterCounts.nBelow40++; - if (protect || physics) { - mClusterCounts.nFakeProtect40++; + if (mQATasks & taskClusterRejection) { + if (mTracking && clNative) { + const auto& cl = clNative->clustersLinear[i]; + mClRej[0]->Fill(cl.getPad() - GPUTPCGeometry::NPads(iRow) / 2 + 0.5, iRow, 1.f); + if (!r.unattached && !r.protect) { + mClRej[1]->Fill(cl.getPad() - GPUTPCGeometry::NPads(iRow) / 2 + 0.5, iRow, 1.f); + } + } } } } - if (physics) { - mClusterCounts.nPhysics++; - } - if (physics || protect) { - mClusterCounts.nProt++; - } - if (unattached) { - mClusterCounts.nUnattached++; - } } } @@ -1837,7 +1912,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx GPUInfo("QA Time: Cluster Counts:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); } - if (mConfig.dumpToROOT) { + if (mConfig.dumpToROOT && !tracksExternal) { if (!clNative || !mTracking || !mTracking->mIOPtrs.mergedTrackHitAttachment || !mTracking->mIOPtrs.mergedTracks) { throw std::runtime_error("Cannot dump non o2::tpc::clusterNative clusters, need also hit attachmend and GPU tracks"); } @@ -1897,24 +1972,76 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } } + + if (mConfig.compareTrackStatus) { +#ifdef GPUCA_DETERMINISTIC_MODE + if (!mTracking || !mTracking->GetProcessingSettings().deterministicGPUReconstruction) +#endif + { + throw std::runtime_error("Need deterministic processing to compare track status"); + } + std::vector status(mTracking->mIOPtrs.nMergedTracks); + for (uint32_t i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { + const auto& trk = mTracking->mIOPtrs.mergedTracks[i]; + status[i] = trk.OK() && trk.NClusters() && trk.GetParam().GetNDF() > 0 && (mConfig.noMC || (mTrackMCLabels[i].isValid() && !mTrackMCLabels[i].isFake())); + } + if (mConfig.compareTrackStatus == 1) { + std::ofstream("track.status", std::ios::binary).write((char*)status.data(), status.size() * sizeof(status[0])); + } else if (mConfig.compareTrackStatus == 2) { + std::ifstream f("track.status", std::ios::binary | std::ios::ate); + std::vector comp(f.tellg()); + f.seekg(0); + f.read((char*)comp.data(), comp.size()); + + if (comp.size() != status.size()) { + throw std::runtime_error("Number of tracks candidates in track fit in track.status and in current reconstruction differ"); + } + std::vector missing, missingComp; + for (uint32_t i = 0; i < status.size(); i++) { + if (status[i] && !comp[i]) { + missingComp.emplace_back(i); + } + if (comp[i] && !status[i]) { + missing.emplace_back(i); + } + } + auto printer = [](std::vector m, const char* name) { + if (m.size()) { + printf("Missing in %s reconstruction: (%zu)\n", name, m.size()); + for (uint32_t i = 0; i < m.size(); i++) { + if (i) { + printf(", "); + } + printf("%d", m[i]); + } + printf("\n"); + } + }; + printer(missing, "current"); + printer(missingComp, "comparison"); + } + } + mTrackingScratchBuffer.clear(); mTrackingScratchBuffer.shrink_to_fit(); } -void GPUQA::GetName(char* fname, int32_t k) +void GPUQA::GetName(char* fname, int32_t k, bool noDash) { const int32_t nNewInput = mConfig.inputHistogramsOnly ? 0 : 1; if (k || mConfig.inputHistogramsOnly || mConfig.name.size()) { if (!(mConfig.inputHistogramsOnly || k)) { - snprintf(fname, 1024, "%s - ", mConfig.name.c_str()); + snprintf(fname, 1024, "%s%s", mConfig.name.c_str(), noDash ? "" : " - "); } else if (mConfig.compareInputNames.size() > (unsigned)(k - nNewInput)) { - snprintf(fname, 1024, "%s - ", mConfig.compareInputNames[k - nNewInput].c_str()); + snprintf(fname, 1024, "%s%s", mConfig.compareInputNames[k - nNewInput].c_str(), noDash ? "" : " - "); } else { strcpy(fname, mConfig.compareInputs[k - nNewInput].c_str()); if (strlen(fname) > 5 && strcmp(fname + strlen(fname) - 5, ".root") == 0) { fname[strlen(fname) - 5] = 0; } - strcat(fname, " - "); + if (!noDash) { + strcat(fname, " - "); + } } } else { fname[0] = 0; @@ -1974,11 +2101,11 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } std::vector colorNums(COLORCOUNT); - if (!qcout) { + if (!(qcout || mConfig.writeFileExt == "root" || mConfig.writeFileExt == "C")) { [[maybe_unused]] static int32_t initColorsInitialized = initColors(); } for (int32_t i = 0; i < COLORCOUNT; i++) { - colorNums[i] = qcout ? defaultColorNums[i] : mColors[i]->GetNumber(); + colorNums[i] = (qcout || mConfig.writeFileExt == "root" || mConfig.writeFileExt == "C") ? defaultColorNums[i] : mColors[i]->GetNumber(); } bool mcAvail = mcPresent(); @@ -2008,10 +2135,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas / Pads for Efficiency Histograms if (mQATasks & taskTrackingEff) { for (int32_t ii = 0; ii < 6; ii++) { - int32_t i = ii == 5 ? 4 : ii; - snprintf(fname, 1024, "eff_vs_%s_layout", VSPARAMETER_NAMES[ii]); - snprintf(name, 2048, "Efficiency versus %s", VSPARAMETER_NAMES[i]); - mCEff[ii] = createGarbageCollected(fname, name, 0, 0, 700, 700. * 2. / 3.); + snprintf(name, 1024, "eff_vs_%s_layout", VSPARAMETER_NAMES[ii]); + mCEff[ii] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); mCEff[ii]->cd(); float dy = 1. / 2.; mPEff[ii][0] = createGarbageCollected("p0", "", 0.0, dy * 0, 0.5, dy * 1); @@ -2034,15 +2159,12 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas / Pads for Resolution Histograms if (mQATasks & taskTrackingRes) { for (int32_t ii = 0; ii < 7; ii++) { - int32_t i = ii == 5 ? 4 : ii; if (ii == 6) { - snprintf(fname, 1024, "res_integral_layout"); - snprintf(name, 2048, "Integral Resolution"); + snprintf(name, 1024, "res_integral_layout"); } else { - snprintf(fname, 1024, "res_vs_%s_layout", VSPARAMETER_NAMES[ii]); - snprintf(name, 2048, "Resolution versus %s", VSPARAMETER_NAMES[i]); + snprintf(name, 1024, "res_vs_%s_layout", VSPARAMETER_NAMES[ii]); } - mCRes[ii] = createGarbageCollected(fname, name, 0, 0, 700, 700. * 2. / 3.); + mCRes[ii] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); mCRes[ii]->cd(); gStyle->SetOptFit(1); @@ -2075,16 +2197,12 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas / Pads for Pull Histograms if (mQATasks & taskTrackingResPull) { for (int32_t ii = 0; ii < 7; ii++) { - int32_t i = ii == 5 ? 4 : ii; - if (ii == 6) { - snprintf(fname, 1024, "pull_integral_layout"); - snprintf(name, 2048, "Integral Pull"); + snprintf(name, 1024, "pull_integral_layout"); } else { - snprintf(fname, 1024, "pull_vs_%s_layout", VSPARAMETER_NAMES[ii]); - snprintf(name, 2048, "Pull versus %s", VSPARAMETER_NAMES[i]); + snprintf(name, 1024, "pull_vs_%s_layout", VSPARAMETER_NAMES[ii]); } - mCPull[ii] = createGarbageCollected(fname, name, 0, 0, 700, 700. * 2. / 3.); + mCPull[ii] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); mCPull[ii]->cd(); gStyle->SetOptFit(1); @@ -2117,8 +2235,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas for Cluster Histos if (mQATasks & taskClusterAttach) { for (int32_t i = 0; i < 3; i++) { - snprintf(fname, 1024, "clusters_%s_layout", CLUSTER_TYPES[i]); - mCClust[i] = createGarbageCollected(fname, CLUSTER_TITLES[i], 0, 0, 700, 700. * 2. / 3.); + snprintf(name, 1024, "clusters_%s_layout", CLUSTER_TYPES[i]); + mCClust[i] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); mCClust[i]->cd(); mPClust[i] = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPClust[i]->Draw(); @@ -2130,16 +2248,16 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas for track statistic histos if (mQATasks & taskTrackStatistics) { - mCTracks = createGarbageCollected("ctrackspt", "Track Pt", 0, 0, 700, 700. * 2. / 3.); - mCTracks->cd(); - mPTracks = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); - mPTracks->Draw(); - mLTracks = createGarbageCollected(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLTracks); + mCTrackPt = createGarbageCollected("ctrackspt", "ctrackspt", 0, 0, 700, 700. * 2. / 3.); + mCTrackPt->cd(); + mPTrackPt = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); + mPTrackPt->Draw(); + mLTrackPt = createGarbageCollected(0.9 - legendSpacingString * 1.5, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); + SetLegend(mLTrackPt, true); for (int32_t i = 0; i < 2; i++) { snprintf(name, 2048, "ctrackst0%d", i); - mCT0[i] = createGarbageCollected(name, "Track T0", 0, 0, 700, 700. * 2. / 3.); + mCT0[i] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); mCT0[i]->cd(); mPT0[i] = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPT0[i]->Draw(); @@ -2147,19 +2265,43 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) SetLegend(mLT0[i]); snprintf(name, 2048, "cncl%d", i); - mCNCl[i] = createGarbageCollected(name, i ? "Number of clusters (corrected for multiple per row)" : "Number of clusters per track", 0, 0, 700, 700. * 2. / 3.); + mCNCl[i] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); mCNCl[i]->cd(); mPNCl[i] = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPNCl[i]->Draw(); - mLNCl[i] = createGarbageCollected(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLNCl[i]); + mLNCl[i] = createGarbageCollected(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); // TODO: Fix sizing of legend, and also fix font size + SetLegend(mLNCl[i], true); } - mCClXY = createGarbageCollected("clxy", "Number of clusters per X / Y", 0, 0, 700, 700. * 2. / 3.); + mCClXY = createGarbageCollected("clxy", "clxy", 0, 0, 700, 700. * 2. / 3.); mCClXY->cd(); mPClXY = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPClXY->Draw(); } + + if (mQATasks & taskClusterRejection) { + for (int32_t i = 0; i < 3; i++) { + snprintf(name, 2048, "cnclrej%d", i); + mCClRej[i] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); + mCClRej[i]->cd(); + mPClRej[i] = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); + mPClRej[i]->Draw(); + } + mCClRejP = createGarbageCollected("cnclrejp", "cnclrejp", 0, 0, 700, 700. * 2. / 3.); + mCClRejP->cd(); + mPClRejP = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); + mPClRejP->Draw(); + } + + if (mQATasks & taskClusterAttach) { + for (int32_t i = 0; i < 4; i++) { + snprintf(name, 2048, "cpadrow%d", i); + mCPadRow[i] = createGarbageCollected(name, name, 0, 0, 700, 700. * 2. / 3.); + mCPadRow[i]->cd(); + mPPadRow[i] = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); + mPPadRow[i]->Draw(); + } + } } if (mConfig.enableLocalOutput && !mConfig.inputHistogramsOnly && (mQATasks & taskTrackingEff) && mcPresent()) { @@ -2189,7 +2331,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Divide eff, compute all for fake/clone auto oldLevel = gErrorIgnoreLevel; gErrorIgnoreLevel = kError; - mEffResult[0][j / 2][j % 2][i]->Divide(mEff[l][j / 2][j % 2][i], mEff[3][j / 2][j % 2][i], "cl=0.683 b(1,1) mode"); + mEffResult[0][j / 2][j % 2][i]->Divide(mEff[l][j / 2][j % 2][i], mEff[5][j / 2][j % 2][i], "cl=0.683 b(1,1) mode"); gErrorIgnoreLevel = oldLevel; mEff[3][j / 2][j % 2][i]->Reset(); // Sum up rec + clone + fake for fake rate mEff[3][j / 2][j % 2][i]->Add(mEff[0][j / 2][j % 2][i]); @@ -2235,13 +2377,12 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } e->SetMarkerColor(kBlack); - e->SetLineColor(colorNums[(l == 2 ? (ConfigNumInputs * 2 + k) : (k * 2 + l)) % COLORCOUNT]); + e->SetLineColor(colorNums[(k < 3 ? (l * 3 + k) : (k * 3 + l)) % COLORCOUNT]); e->GetHistogram()->GetYaxis()->SetRangeUser(-0.02, 1.02); e->Draw(k || l ? "same P" : "AP"); if (j == 0) { GetName(fname, k); - snprintf(name, 2048, "%s%s", fname, EFF_NAMES[l]); - mLEff[ii]->AddEntry(e, name, "l"); + mLEff[ii]->AddEntry(e, Form("%s%s", fname, EFF_NAMES[l]), "l"); } } if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { @@ -2264,9 +2405,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } doPerfFigure(0.2, 0.295, 0.025); - mCEff[ii]->Print(Form("plots/eff_vs_%s.pdf", VSPARAMETER_NAMES[ii])); - if (mConfig.writeRootFiles) { - mCEff[ii]->Print(Form("plots/eff_vs_%s.root", VSPARAMETER_NAMES[ii])); + mCEff[ii]->Print(Form("%s/eff_vs_%s.pdf", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii])); + if (mConfig.writeFileExt != "") { + mCEff[ii]->Print(Form("%s/eff_vs_%s.%s", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii], mConfig.writeFileExt.c_str())); } } } @@ -2373,10 +2514,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) if (mConfig.inputHistogramsOnly) { dstIntegral = createGarbageCollected(); } - snprintf(fname, 1024, p ? "IntPull%s" : "IntRes%s", VSPARAMETER_NAMES[j]); - snprintf(name, 2048, p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j]); - dstIntegral->SetName(fname); - dstIntegral->SetTitle(name); + dstIntegral->SetName(Form(p ? "IntPull%s" : "IntRes%s", VSPARAMETER_NAMES[j])); + dstIntegral->SetTitle(Form(p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j])); } if (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) { pad->cd(); @@ -2427,8 +2566,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) for (int32_t l = 0; l < 2; l++) { TH1F* e = dst[l]; if (!mConfig.inputHistogramsOnly && k == 0) { - snprintf(name, 2048, p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j]); - e->SetTitle(name); + e->SetTitle(Form(p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j])); e->SetStats(kFALSE); if (tout) { if (l == 0) { @@ -2468,12 +2606,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->Draw(k || l ? "same" : ""); if (j == 0) { GetName(fname, k); - if (p) { - snprintf(name, 2048, "%s%s", fname, l ? "Mean" : "Pull"); - } else { - snprintf(name, 2048, "%s%s", fname, l ? "Mean" : "Resolution"); - } - leg->AddEntry(e, name, "l"); + leg->AddEntry(e, Form("%s%s", fname, l ? "Mean" : (p ? "Pull" : "Resolution")), "l"); } } } @@ -2502,9 +2635,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } doPerfFigure(0.2, 0.295, 0.025); - can->Print(Form(p ? "plots/pull_vs_%s.pdf" : "plots/res_vs_%s.pdf", VSPARAMETER_NAMES[ii])); - if (mConfig.writeRootFiles) { - can->Print(Form(p ? "plots/pull_vs_%s.root" : "plots/res_vs_%s.root", VSPARAMETER_NAMES[ii])); + can->Print(Form(p ? "%s/pull_vs_%s.pdf" : "%s/res_vs_%s.pdf", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii])); + if (mConfig.writeFileExt != "") { + can->Print(Form(p ? "%s/pull_vs_%s.%s" : "%s/res_vs_%s.%s", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii], mConfig.writeFileExt.c_str())); } } } @@ -2573,9 +2706,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } - can->Print(p ? "plots/pull_integral.pdf" : "plots/res_integral.pdf"); - if (mConfig.writeRootFiles) { - can->Print(p ? "plots/pull_integral.root" : "plots/res_integral.root"); + can->Print(Form(p ? "%s/pull_integral.pdf" : "%s/res_integral.pdf", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + can->Print(Form(p ? "%s/pull_integral.%s" : "%s/res_integral.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } } } @@ -2668,7 +2801,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } - e->SetTitle(CLUSTER_TITLES[i]); + e->SetTitle(mConfig.plotsNoTitle ? "" : CLUSTER_TITLES[i]); e->GetYaxis()->SetTitle(i == 0 ? "Number of TPC clusters" : i == 1 ? "Fraction of TPC clusters" : CLUST_HIST_INT_SUM ? "Total TPC clusters (integrated)" : "Fraction of TPC clusters (integrated)"); e->GetXaxis()->SetTitle("#it{p}_{Tmc} (GeV/#it{c})"); e->GetXaxis()->SetTitleOffset(1.1); @@ -2693,8 +2826,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetLineColor(colorNums[numColor++ % COLORCOUNT]); e->Draw(j == end - 1 && k == 0 ? "" : "same"); GetName(fname, k); - snprintf(name, 2048, "%s%s", fname, CLUSTER_NAMES[j - begin]); - mLClust[i]->AddEntry(e, name, "l"); + mLClust[i]->AddEntry(e, Form("%s%s", fname, CLUSTER_NAMES[j - begin]), "l"); } } if (ConfigNumInputs == 1) { @@ -2723,11 +2855,39 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) if (!mConfig.enableLocalOutput) { continue; } - doPerfFigure(i != 2 ? 0.37 : 0.6, 0.295, 0.030); + doPerfFigure(i == 0 ? 0.37 : (i == 1 ? 0.34 : 0.6), 0.295, 0.030); mCClust[i]->cd(); - mCClust[i]->Print(i == 2 ? "plots/clusters_integral.pdf" : i == 1 ? "plots/clusters_relative.pdf" : "plots/clusters.pdf"); - if (mConfig.writeRootFiles) { - mCClust[i]->Print(i == 2 ? "plots/clusters_integral.root" : i == 1 ? "plots/clusters_relative.root" : "plots/clusters.root"); + mCClust[i]->Print(Form(i == 2 ? "%s/clusters_integral.pdf" : i == 1 ? "%s/clusters_relative.pdf" : "%s/clusters.pdf", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + mCClust[i]->Print(Form(i == 2 ? "%s/clusters_integral.%s" : i == 1 ? "%s/clusters_relative.%s" : "%s/clusters.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); + } + } + + for (int32_t i = 0; i < 4; i++) { + auto* e = mPadRow[i]; + if (tout && !mConfig.inputHistogramsOnly) { + e->Write(); + } + mPPadRow[i]->cd(); + e->SetOption("colz"); + std::string title = "First Track Pad Row (p_{T} > 1GeV, N_{Cl} #geq " + std::to_string(PADROW_CHECK_MINCLS); + if (i >= 2) { + title += ", row_{trk} > row_{MC} + 3, row_{MC} < 10"; + } + if (i >= 3) { + title += ", #Phi_{Cl} < 0.15"; + } + title += ")"; + + e->SetTitle(mConfig.plotsNoTitle ? "" : title.c_str()); + e->GetXaxis()->SetTitle(i == 3 ? "Local Occupancy" : (i ? "#Phi_{Cl} (sector)" : "First MC Pad Row")); + e->GetYaxis()->SetTitle("First Pad Row"); + e->Draw(); + mCPadRow[i]->cd(); + static const constexpr char* PADROW_NAMES[4] = {"MC", "Phi", "Phi1", "Occ"}; + mCPadRow[i]->Print(Form("%s/padRow%s.pdf", mConfig.plotsDir.c_str(), PADROW_NAMES[i])); + if (mConfig.writeFileExt != "") { + mCPadRow[i]->Print(Form("%s/padRow%s.%s", mConfig.plotsDir.c_str(), PADROW_NAMES[i], mConfig.writeFileExt.c_str())); } } } @@ -2751,7 +2911,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Process track statistic histograms float tmpMax = 0.; for (int32_t k = 0; k < ConfigNumInputs; k++) { // TODO: Simplify this drawing, avoid copy&paste - TH1F* e = mTracks; + TH1F* e = mTrackPt; if (GetHist(e, tin, k, nNewInput) == nullptr) { continue; } @@ -2760,10 +2920,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) tmpMax = e->GetMaximum(); } } - mPTracks->cd(); - mPTracks->SetLogx(); + mPTrackPt->cd(); + mPTrackPt->SetLogx(); for (int32_t k = 0; k < ConfigNumInputs; k++) { - TH1F* e = mTracks; + TH1F* e = mTrackPt; if (GetHist(e, tin, k, nNewInput) == nullptr) { continue; } @@ -2774,23 +2934,25 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->GetYaxis()->SetTitle("a.u."); - e->GetXaxis()->SetTitle("#it{p}_{Tmc} (GeV/#it{c})"); + e->SetTitle(mConfig.plotsNoTitle ? "" : "Number of Tracks vs #it{p}_{T}"); + e->GetYaxis()->SetTitle("Number of Tracks"); + e->GetXaxis()->SetTitle("#it{p}_{T} (GeV/#it{c})"); + e->GetXaxis()->SetTitleOffset(1.2); if (qcout) { qcout->Add(e); } e->SetMarkerColor(kBlack); e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); - GetName(fname, k); - snprintf(name, 2048, "%sTrack Pt", fname); - mLTracks->AddEntry(e, name, "l"); + GetName(fname, k, mConfig.inputHistogramsOnly); + mLTrackPt->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname), "l"); } - mLTracks->Draw(); - mCTracks->cd(); - mCTracks->Print("plots/tracks.pdf"); - if (mConfig.writeRootFiles) { - mCTracks->Print("plots/tracks.root"); + mLTrackPt->Draw(); + doPerfFigure(0.63, 0.7, 0.030); + mCTrackPt->cd(); + mCTrackPt->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + mCTrackPt->Print(Form("%s/tracks.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } for (int32_t i = 0; i < 2; i++) { @@ -2818,25 +2980,24 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); + e->SetTitle(mConfig.plotsNoTitle ? "" : (i ? "Track t_{0} resolution" : "Track t_{0} distribution")); e->GetYaxis()->SetTitle("a.u."); - e->GetXaxis()->SetTitle(i ? "to vs t0_{mc}" : "t0"); + e->GetXaxis()->SetTitle(i ? "t_{0} - t_{0, mc}" : "t_{0}"); if (qcout) { qcout->Add(e); } e->SetMarkerColor(kBlack); e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); - GetName(fname, k); - snprintf(name, 2048, "%sTrack T0 %s", fname, i ? "" : "resolution"); - mLT0[i]->AddEntry(e, name, "l"); + GetName(fname, k, mConfig.inputHistogramsOnly); + mLT0[i]->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s (%s)" : "%sTrack t_{0} %s", fname, i ? "" : "resolution"), "l"); } mLT0[i]->Draw(); + doPerfFigure(0.63, 0.7, 0.030); mCT0[i]->cd(); - snprintf(name, 2048, "plots/t0%s.pdf", i ? "_res" : ""); - mCT0[i]->Print(name); - if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/t0%s.root", i ? "_res" : ""); - mCT0[i]->Print(name); + mCT0[i]->Print(Form("%s/t0%s.pdf", mConfig.plotsDir.c_str(), i ? "_res" : "")); + if (mConfig.writeFileExt != "") { + mCT0[i]->Print(Form("%s/t0%s.%s", mConfig.plotsDir.c_str(), i ? "_res" : "", mConfig.writeFileExt.c_str())); } tmpMax = 0.; @@ -2863,35 +3024,90 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); + e->SetTitle(mConfig.plotsNoTitle ? "" : (i ? "Number of Rows with attached Cluster" : "Number of Clusters")); e->GetYaxis()->SetTitle("a.u."); - e->GetXaxis()->SetTitle("NClusters"); + e->GetXaxis()->SetTitle(i ? "N_{Rows with Clusters}" : "N_{Clusters}"); if (qcout) { qcout->Add(e); } e->SetMarkerColor(kBlack); e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); - GetName(fname, k); - snprintf(name, 2048, "%sNClusters%d", fname, i); - mLNCl[i]->AddEntry(e, name, "l"); + GetName(fname, k, mConfig.inputHistogramsOnly); + mLNCl[i]->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : (i ? "%sN_{Clusters}" : "%sN_{Rows with Clusters}"), fname), "l"); } mLNCl[i]->Draw(); + doPerfFigure(0.6, 0.7, 0.030); mCNCl[i]->cd(); - snprintf(name, 2048, "plots/nClusters%s.pdf", i ? "_corrected" : ""); - mCNCl[i]->Print(name); - if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/nClusters%s.root", i ? "_corrected" : ""); - mCNCl[i]->Print(name); + mCNCl[i]->Print(Form("%s/nClusters%s.pdf", mConfig.plotsDir.c_str(), i ? "_corrected" : "")); + if (mConfig.writeFileExt != "") { + mCNCl[i]->Print(Form("%s/nClusters%s.%s", mConfig.plotsDir.c_str(), i ? "_corrected" : "", mConfig.writeFileExt.c_str())); } } - mPClXY->cd(); + mPClXY->cd(); // TODO: This should become a separate task category mClXY->SetOption("colz"); mClXY->Draw(); mCClXY->cd(); - mCClXY->Print("plots/clustersXY.pdf"); - if (mConfig.writeRootFiles) { - mCClXY->Print("plots/clustersXY.root"); + mCClXY->Print(Form("%s/clustersXY.pdf", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + mCClXY->Print(Form("%s/clustersXY.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); + } + } + + if (mQATasks & taskClusterRejection) { + mClRej[2]->Divide(mClRej[1], mClRej[0]); + + for (int32_t i = 0; i < 3; i++) { + if (tout && !mConfig.inputHistogramsOnly) { + mClRej[i]->Write(); + } + mPClRej[i]->cd(); + mClRej[i]->SetTitle(mConfig.plotsNoTitle ? "" : REJECTED_NAMES[i]); + mClRej[i]->SetOption("colz"); + mClRej[i]->Draw(); + mCClRej[i]->cd(); + mCClRej[i]->Print(Form("%s/clustersRej%d%s.pdf", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i])); + if (mConfig.writeFileExt != "") { + mCClRej[i]->Print(Form("%s/clustersRej%d%s.%s", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i], mConfig.writeFileExt.c_str())); + } + } + + mPClRejP->cd(); + for (int32_t k = 0; k < ConfigNumInputs; k++) { + auto* tmp = mClRej[0]; + if (GetHist(tmp, tin, k, nNewInput) == nullptr) { + continue; + } + TH1D* proj1 = tmp->ProjectionY(Form("clrejptmp1%d", k)); // TODO: Clean up names + proj1->SetDirectory(nullptr); + tmp = mClRej[1]; + if (GetHist(tmp, tin, k, nNewInput) == nullptr) { + continue; + } + TH1D* proj2 = tmp->ProjectionY(Form("clrejptmp2%d", k)); + proj2->SetDirectory(nullptr); + + auto* e = mClRejP; + if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; + } + e->Divide(proj2, proj1); + if (tout && !mConfig.inputHistogramsOnly && k == 0) { + e->Write(); + } + delete proj1; + delete proj2; + e->SetMinimum(-0.02); + e->SetMaximum(0.22); + e->SetTitle(mConfig.plotsNoTitle ? "" : "Rejected Clusters"); + e->GetXaxis()->SetTitle("Pad Row"); + e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); + e->Draw(k == 0 ? "" : "same"); + } + mPClRejP->Print(Form("%s/clustersRejProjected.pdf", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + mPClRejP->Print(Form("%s/clustersRejProjected.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } } @@ -2914,7 +3130,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) if (!qcout) { clearGarbagageCollector(); } - GPUInfo("GPU TPC QA histograms have been written to %s files", mConfig.writeRootFiles ? ".pdf and .root" : ".pdf"); + GPUInfo("GPU TPC QA histograms have been written to pdf%s%s files", mConfig.writeFileExt == "" ? "" : " and ", mConfig.writeFileExt.c_str()); gErrorIgnoreLevel = oldRootIgnoreLevel; return (0); } @@ -2936,7 +3152,9 @@ void GPUQA::PrintClusterCount(int32_t mode, int32_t& num, const char* name, uint createHist(mHistClusterCount[num], name2, name, 1000, 0, mConfig.histMaxNClusters, 1000, 0, 100); } else if (mode == 0) { if (normalization && mConfig.enableLocalOutput) { - printf("\t%40s: %'12" PRIu64 " (%6.2f%%)\n", name, n, 100.f * n / normalization); + for (uint32_t i = 0; i < 1 + (mTextDump != nullptr); i++) { + fprintf(i ? mTextDump : stdout, "\t%40s: %'12" PRIu64 " (%6.2f%%)\n", name, n, 100.f * n / normalization); + } } if (mConfig.clusterRejectionHistograms) { float ratio = 100.f * n / std::max(normalization, 1); @@ -2948,9 +3166,12 @@ void GPUQA::PrintClusterCount(int32_t mode, int32_t& num, const char* name, uint int32_t GPUQA::DoClusterCounts(uint64_t* attachClusterCounts, int32_t mode) { + if (mConfig.enableLocalOutput && !mConfig.inputHistogramsOnly && mConfig.plotsDir != "") { + mTextDump = fopen((mConfig.plotsDir + "/clusterCounts.txt").c_str(), "w+"); + } int32_t num = 0; if (mcPresent() && (mQATasks & taskClusterAttach) && attachClusterCounts) { - for (int32_t i = 0; i < N_CLS_HIST; i++) { + for (int32_t i = 0; i < N_CLS_HIST; i++) { // TODO: Check that these counts are still printed correctly! PrintClusterCount(mode, num, CLUSTER_NAMES[i], attachClusterCounts[i], mClusterCounts.nTotal); } PrintClusterCount(mode, num, "Unattached", attachClusterCounts[N_CLS_HIST - 1] - attachClusterCounts[CL_att_adj], mClusterCounts.nTotal); @@ -2986,6 +3207,10 @@ int32_t GPUQA::DoClusterCounts(uint64_t* attachClusterCounts, int32_t mode) PrintClusterCount(mode, num, "Correctly Attached all-trk normalized", mClusterCounts.nCorrectlyAttachedNormalized, mClusterCounts.nTotal); PrintClusterCount(mode, num, "Correctly Attached non-fake normalized", mClusterCounts.nCorrectlyAttachedNormalizedNonFake, mClusterCounts.nTotal); } + if (mTextDump) { + fclose(mTextDump); + mTextDump = nullptr; + } return num; } diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index 92e931892339a..bd3c9be3a9aa5 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -16,6 +16,7 @@ #define GPUQA_H #include "GPUSettings.h" +#include "GPUDataTypesQA.h" struct AliHLTTPCClusterMCWeight; class TH1F; class TH2F; @@ -62,6 +63,8 @@ class GPUQA #else #include "GPUTPCDef.h" +#include "GPUDataTypesIO.h" +#include #include #include #include @@ -86,6 +89,7 @@ namespace o2::gpu class GPUChainTracking; struct GPUParam; struct GPUTPCMCInfo; +struct checkClusterStateResult; namespace internal { struct GPUQAGarbageCollection; @@ -144,17 +148,7 @@ class GPUQA static constexpr int32_t MC_LABEL_INVALID = -1e9; - enum QA_TASKS { - taskTrackingEff = 1, - taskTrackingRes = 2, - taskTrackingResPull = 4, - taskClusterAttach = 8, - taskTrackStatistics = 16, - taskClusterCounts = 32, - taskDefault = 63, - taskDefaultPostprocess = 31, - tasksNoQC = 56 - }; + using enum gpudatatypes::gpuqa::gpuQATaskIds; private: struct additionalMCParameters { @@ -172,12 +166,12 @@ class GPUQA void CopyO2MCtoIOPtr(GPUTrackingInOutPointers* ptr); template void SetAxisSize(T* e); - void SetLegend(TLegend* l); + void SetLegend(TLegend* l, bool bigText = false); double* CreateLogAxis(int32_t nbins, float xmin, float xmax); void ChangePadTitleSize(TPad* p, float size); void DrawHisto(TH1* histo, char* filename, char* options); void doPerfFigure(float x, float y, float size); - void GetName(char* fname, int32_t k); + void GetName(char* fname, int32_t k, bool noDash = false); template T* GetHist(T*& ee, std::vector>& tin, int32_t k, int32_t nNewInput); @@ -230,13 +224,13 @@ class GPUQA const auto& GetClusterLabels(); bool mcPresent(); + template + checkClusterStateResult checkClusterState(uint32_t attach, T* counts = nullptr) const; + GPUChainTracking* mTracking; const GPUSettingsQA& mConfig; const GPUParam* mParam; - const char* str_perf_figure_1 = "ALICE Performance 2018/03/20"; - // const char* str_perf_figure_2 = "2015, MC pp, #sqrt{s} = 5.02 TeV"; - const char* str_perf_figure_2 = "2015, MC Pb-Pb, #sqrt{s_{NN}} = 5.02 TeV"; //------------------------- std::vector mTrackMCLabels; @@ -259,7 +253,7 @@ class GPUQA std::vector mClusterParam; int32_t mNTotalFakes = 0; - TH1F* mEff[5][2][2][5]; // eff,clone,fake,all,all-fake - findable - secondaries - y,z,phi,eta,pt - work,result + TH1F* mEff[6][2][2][5]; // eff,clone,fake,all,all-fake - findable - secondaries - y,z,phi,eta,pt - work,result TGraphAsymmErrors* mEffResult[4][2][2][5]; TCanvas* mCEff[6]; TPad* mPEff[6][4]; @@ -296,10 +290,10 @@ class GPUQA double nUnaccessible = 0; } mClusterCounts; - TH1F* mTracks; - TCanvas* mCTracks; - TPad* mPTracks; - TLegend* mLTracks; + TH1F* mTrackPt; + TCanvas* mCTrackPt; + TPad* mPTrackPt; + TLegend* mLTrackPt; TH1F* mNCl[2]; TCanvas* mCNCl[2]; @@ -315,6 +309,17 @@ class GPUQA TCanvas* mCClXY; TPad* mPClXY; + TH2F* mClRej[3]; + TH1D* mClRejP; + TCanvas* mCClRej[3]; + TCanvas* mCClRejP; + TPad* mPClRej[3]; + TPad* mPClRejP; + + TH2F* mPadRow[4]; + TCanvas* mCPadRow[4]; + TPad* mPPadRow[4]; + std::vector mHistClusterCount; std::vector* mHist1D = nullptr; @@ -353,6 +358,7 @@ class GPUQA int32_t mMCTrackMin = -1, mMCTrackMax = -1; const o2::tpc::ClusterNativeAccess* mClNative = nullptr; + FILE* mTextDump = nullptr; }; inline bool GPUQA::SuppressTrack(int32_t iTrack) const { return (mConfig.matchMCLabels.size() && !mGoodTracks[mNEvents][iTrack]); } diff --git a/GPU/GPUTracking/utils/VcShim.h b/GPU/GPUTracking/utils/VcShim.h new file mode 100644 index 0000000000000..21a9a6a5c95c2 --- /dev/null +++ b/GPU/GPUTracking/utils/VcShim.h @@ -0,0 +1,192 @@ +// Copyright 2020-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 VcShim.h +/// \brief Provides a basic fallback implementation for Vc +/// +/// \author Felix Weiglhofer + +#ifndef GPU_UTILS_VCSHIM_H +#define GPU_UTILS_VCSHIM_H + +#ifndef GPUCA_NO_VC + +#include + +#else + +#include +#include +#include +#include + +namespace Vc +{ + +constexpr struct VectorSpecialInitializerZero { +} Zero; +constexpr struct AlignedTag { +} Aligned; + +template +typename T::vector_type& internal_data(T& v) +{ + return v.mData; +} + +template +const typename T::vector_type& internal_data(const T& v) +{ + return v.mData; +} + +namespace Common +{ + +template +class WriteMaskVector +{ + private: + const M& mMask; + V& mVec; + + public: + using value_type = typename V::value_type; + + WriteMaskVector(V& v, const M& m) : mMask(m), mVec(v) {} + + WriteMaskVector& operator++(int) + { + for (size_t i = 0; i < mVec.size(); i++) + mVec[i] += value_type(mMask[i]); + return *this; + } + + WriteMaskVector& operator=(const value_type& v) + { + for (size_t i = 0; i < mVec.size(); i++) { + if (mMask[i]) + mVec[i] = v; + } + return *this; + } +}; + +inline void prefetchMid(const void*) {} +inline void prefetchFar(const void*) {} +inline void prefetchForOneRead(const void*) {} + +} // namespace Common + +template +class fixed_size_simd_mask +{ + private: + std::bitset mData; + + public: + bool isNotEmpty() const { return mData.any(); } + + std::bitset::reference operator[](size_t i) { return mData[i]; } + bool operator[](size_t i) const { return mData[i]; } + + fixed_size_simd_mask operator!() const + { + auto o = *this; + o.mData.flip(); + return o; + } +}; + +template +class fixed_size_simd +{ + private: + std::array mData; + + public: + using vector_type = std::array; + using value_type = T; + using mask_type = fixed_size_simd_mask; + + static constexpr size_t size() { return N; } + + fixed_size_simd() = default; + explicit fixed_size_simd(VectorSpecialInitializerZero) { mData = {}; } + + template + fixed_size_simd(const fixed_size_simd& w) + { + std::copy_n(internal_data(w).begin(), N, mData.begin()); + } + + fixed_size_simd(const T* d, AlignedTag) { std::copy_n(d, N, mData.begin()); } + + T& operator[](size_t i) { return mData[i]; } + const T& operator[](size_t i) const { return mData[i]; } + + Common::WriteMaskVector operator()(const mask_type& m) { return {*this, m}; } + + fixed_size_simd& operator=(const T& v) + { + for (auto& x : mData) + x = v; + return *this; + } + + fixed_size_simd& operator+=(const T& v) + { + for (auto& x : mData) + x += v; + return *this; + } + + fixed_size_simd& operator/=(const T& v) + { + for (auto& x : mData) + x /= v; + return *this; + } + + fixed_size_simd operator/(const T& v) const + { + auto x = *this; + return x /= v; + } + + mask_type operator==(const T& v) const + { + mask_type m; + for (size_t i = 0; i < N; i++) + m[i] = mData[i] == v; + return m; + } + + mask_type operator!=(const T& v) const { return !(*this == v); } + + friend vector_type& internal_data<>(fixed_size_simd& x); + friend const vector_type& internal_data<>(const fixed_size_simd& x); +}; + +template +V max(const V& a, const V& b) +{ + V o; + for (size_t i = 0; i < a.size(); i++) + o[i] = std::max(a[i], b[i]); + return o; +} + +} // namespace Vc + +#endif // ifndef GPUCA_NO_VC + +#endif diff --git a/GPU/GPUTracking/utils/makefile_opencl_compiler.cxx b/GPU/GPUTracking/utils/makefile_opencl_compiler.cxx deleted file mode 100644 index f6400cc3369e0..0000000000000 --- a/GPU/GPUTracking/utils/makefile_opencl_compiler.cxx +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file makefile_opencl_compiler.cxx -/// \author David Rohr - -#define CL_TARGET_OPENCL_VERSION 220 -#define _CRT_SECURE_NO_WARNINGS -#include "CL/opencl.h" -#include -#include -#include -#include -#include - -#include "opencl_compiler_structs.h" - -#define quit(arg) \ - { \ - fprintf(stderr, arg "\n"); \ - return (1); \ - } -#define DEFAULT_OPENCL_COMPILER_OPTIONS "" -#define DEFAULT_OUTPUT_FILE "opencl.out" - -int32_t main(int argc, char** argv) -{ - const char* output_file = DEFAULT_OUTPUT_FILE; - std::string compiler_options = DEFAULT_OPENCL_COMPILER_OPTIONS; - std::vector files; - - printf("Passing command line options:\n"); - bool add_option = false; - for (int32_t i = 1; i < argc; i++) { - if (add_option) { - compiler_options += " "; - compiler_options += argv[i]; - } else if (strcmp(argv[i], "--") == 0) { - add_option = true; - } else if (strcmp(argv[i], "-output-file") == 0) { - if (++i >= argc) { - quit("Output file name missing"); - } - output_file = argv[i]; - } else { - fprintf(stderr, "%s\n", argv[i]); - files.push_back(argv[i]); - } - } - - cl_int ocl_error; - cl_uint num_platforms; - if (clGetPlatformIDs(0, nullptr, &num_platforms) != CL_SUCCESS) { - quit("Error getting OpenCL Platform Count"); - } - if (num_platforms == 0) { - quit("No OpenCL Platform found"); - } - printf("%d OpenCL Platforms found\n", num_platforms); - - // Query platforms - cl_platform_id* platforms = new cl_platform_id[num_platforms]; - if (platforms == nullptr) { - quit("Memory allocation error"); - } - if (clGetPlatformIDs(num_platforms, platforms, nullptr) != CL_SUCCESS) { - quit("Error getting OpenCL Platforms"); - } - - cl_platform_id platform; - bool found = false; - - _makefiles_opencl_platform_info pinfo; - for (uint32_t i_platform = 0; i_platform < num_platforms; i_platform++) { - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_PROFILE, 64, pinfo.platform_profile, nullptr); - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_VERSION, 64, pinfo.platform_version, nullptr); - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_NAME, 64, pinfo.platform_name, nullptr); - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_VENDOR, 64, pinfo.platform_vendor, nullptr); - printf("Available Platform %u: (%s %s) %s %s\n", i_platform, pinfo.platform_profile, pinfo.platform_version, pinfo.platform_vendor, pinfo.platform_name); - if (strcmp(pinfo.platform_vendor, "Advanced Micro Devices, Inc.") == 0 && strcmp(pinfo.platform_version, "OpenCL 2.0 AMD-APP (1800.8)") == 0) { - found = true; - printf("AMD OpenCL Platform found (%u)\n", i_platform); - platform = platforms[i_platform]; - break; - } - } - if (found == false) { - quit("Did not find AMD OpenCL Platform"); - } - - if (clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0, nullptr, &pinfo.count) != CL_SUCCESS) { - quit("Error getting OPENCL Device Count"); - } - - // Query devices - cl_device_id* devices = new cl_device_id[pinfo.count]; - if (devices == nullptr) { - quit("Memory allocation error"); - } - if (clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, pinfo.count, devices, nullptr) != CL_SUCCESS) { - quit("Error getting OpenCL devices"); - } - - _makefiles_opencl_device_info dinfo; - cl_device_type device_type; - cl_uint freq, shaders; - - printf("Available OPENCL devices:\n"); - for (uint32_t i = 0; i < pinfo.count; i++) { - printf("Examining device %u\n", i); - - clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 64, dinfo.device_name, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR, 64, dinfo.device_vendor, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(cl_device_type), &device_type, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(freq), &freq, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(shaders), &shaders, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_ADDRESS_BITS, sizeof(dinfo.nbits), &dinfo.nbits, nullptr); - printf("Found Device %u : %s %s (Frequency %d, Shaders %d, %d bit)\n", i, dinfo.device_vendor, dinfo.device_name, (int32_t)freq, (int32_t)shaders, (int32_t)dinfo.nbits); - } - - if (files.size() == 0) { - quit("Syntax: opencl [-output-file OUTPUT_FILE] FILE1 [FILE2] ... [FILEn] [-- COMPILER_OPTION_1] [COMPILER_OPTION_2] ... [COMPILER_OPTION_N]"); - } - - char** buffers = (char**)malloc(files.size() * sizeof(char*)); - if (buffers == nullptr) { - quit("Memory allocation error\n"); - } - for (uint32_t i = 0; i < files.size(); i++) { - printf("Reading source file %s\n", files[i]); - FILE* fp = fopen(files[i], "rb"); - if (fp == nullptr) { - printf("Cannot open %s\n", files[i]); - free(buffers); - return (1); - } - fseek(fp, 0, SEEK_END); - size_t file_size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - buffers[i] = (char*)malloc(file_size + 1); - if (buffers[i] == nullptr) { - quit("Memory allocation error"); - } - if (fread(buffers[i], 1, file_size, fp) != file_size) { - quit("Error reading file"); - } - buffers[i][file_size] = 0; - fclose(fp); - } - - printf("Creating OpenCL Context\n"); - // Create OpenCL context - cl_context context = clCreateContext(nullptr, pinfo.count, devices, nullptr, nullptr, &ocl_error); - if (ocl_error != CL_SUCCESS) { - quit("Error creating OpenCL context"); - } - - printf("Creating OpenCL Program Object\n"); - // Create OpenCL program object - cl_program program = clCreateProgramWithSource(context, (cl_uint)files.size(), (const char**)buffers, nullptr, &ocl_error); - if (ocl_error != CL_SUCCESS) { - quit("Error creating program object"); - } - - printf("Compiling OpenCL Program\n"); - // Compile program - ocl_error = clBuildProgram(program, pinfo.count, devices, compiler_options.c_str(), nullptr, nullptr); - if (ocl_error != CL_SUCCESS) { - fprintf(stderr, "OpenCL Error while building program: %d (Compiler options: %s)\n", ocl_error, compiler_options.c_str()); - fprintf(stderr, "OpenCL Kernel:\n\n"); - for (uint32_t i = 0; i < files.size(); i++) { - printf("%s\n\n", buffers[i]); - } - - for (uint32_t i = 0; i < pinfo.count; i++) { - cl_build_status status; - clGetProgramBuildInfo(program, devices[i], CL_PROGRAM_BUILD_STATUS, sizeof(status), &status, nullptr); - if (status == CL_BUILD_ERROR) { - size_t log_size; - clGetProgramBuildInfo(program, devices[i], CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size); - char* build_log = (char*)malloc(log_size + 1); - if (build_log == nullptr) { - quit("Memory allocation error"); - } - clGetProgramBuildInfo(program, devices[i], CL_PROGRAM_BUILD_LOG, log_size, build_log, nullptr); - fprintf(stderr, "Build Log (device %d):\n\n%s\n\n", i, build_log); - free(build_log); - } - } - } - for (uint32_t i = 0; i < files.size(); i++) { - free(buffers[i]); - } - free(buffers); - if (ocl_error != CL_SUCCESS) { - return (1); - } - - printf("Obtaining program binaries\n"); - size_t* binary_sizes = (size_t*)malloc(pinfo.count * sizeof(size_t)); - if (binary_sizes == nullptr) { - quit("Memory allocation error"); - } - clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, pinfo.count * sizeof(size_t), binary_sizes, nullptr); - char** binary_buffers = (char**)malloc(pinfo.count * sizeof(char*)); - if (binary_buffers == nullptr) { - quit("Memory allocation error"); - } - for (uint32_t i = 0; i < pinfo.count; i++) { - printf("Binary size for device %d: %d\n", i, (int32_t)binary_sizes[i]); - binary_buffers[i] = (char*)malloc(binary_sizes[i]); - memset(binary_buffers[i], 0, binary_sizes[i]); - if (binary_buffers[i] == nullptr) { - quit("Memory allocation error"); - } - } - clGetProgramInfo(program, CL_PROGRAM_BINARIES, pinfo.count * sizeof(char*), binary_buffers, nullptr); - - printf("Programs obtained successfully, cleaning up opencl\n"); - clReleaseProgram(program); - clReleaseContext(context); - - printf("Writing binaries to file (%s)\n", output_file); - FILE* fp; - fp = fopen(output_file, "w+b"); - if (fp == nullptr) { - quit("Error opening output file\n"); - } - const char* magic_bytes = "QOCLPB"; - fwrite(magic_bytes, 1, strlen(magic_bytes) + 1, fp); - fwrite(&pinfo, 1, sizeof(pinfo), fp); - for (uint32_t i = 0; i < pinfo.count; i++) { - clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 64, dinfo.device_name, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR, 64, dinfo.device_vendor, nullptr); - dinfo.binary_size = binary_sizes[i]; - fwrite(&dinfo, 1, sizeof(dinfo), fp); - fwrite(binary_buffers[i], 1, binary_sizes[i], fp); - } - fclose(fp); - - printf("All done, cleaning up remaining buffers\n"); - for (uint32_t i = 0; i < pinfo.count; i++) { - free(binary_buffers[i]); - } - free(binary_sizes); - free(binary_buffers); - - return (0); -} diff --git a/GPU/GPUTracking/utils/opencl_obtain_program.h b/GPU/GPUTracking/utils/opencl_obtain_program.h deleted file mode 100644 index 6c10ca9d47de1..0000000000000 --- a/GPU/GPUTracking/utils/opencl_obtain_program.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file opencl_obtain_program.h -/// \author David Rohr - -#ifndef MAKEFILES_OPENCL_OBTAIN_PROGRAMH -#define MAKEFILES_OPENCL_OBTAIN_PROGRAMH - -#include -#include -#include "opencl_compiler_structs.h" - -static int32_t _makefiles_opencl_obtain_program_helper(cl_context context, cl_uint num_devices, cl_device_id* devices, cl_program* program, char* binaries) -{ - const char* magic_bytes = "QOCLPB"; - if (strncmp(magic_bytes, binaries, strlen(magic_bytes)) != 0) { - printf("Internal error accessing opencl program\n"); - return (1); - } - char* current_ptr = binaries + strlen(magic_bytes) + 1; - _makefiles_opencl_platform_info* pinfo = (_makefiles_opencl_platform_info*)current_ptr; - current_ptr += sizeof(_makefiles_opencl_platform_info); - - if (num_devices != pinfo->count) { - printf("Number of devices differs from number of devices in opencl program\n"); - return (1); - } - // printf("Obtaining program for OpenCL Platform: (%s %s) %s %s\n", pinfo->platform_profile, pinfo->platform_version, pinfo->platform_vendor, pinfo->platform_name); - - std::vector program_sizes(pinfo->count); - std::vector program_binaries(pinfo->count); - - for (uint32_t i = 0; i < pinfo->count; i++) { - char device_name[64], device_vendor[64]; - cl_uint nbits; - clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 64, device_name, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR, 64, device_vendor, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_ADDRESS_BITS, sizeof(nbits), &nbits, nullptr); - _makefiles_opencl_device_info* dinfo = (_makefiles_opencl_device_info*)current_ptr; - if (strcmp(device_name, dinfo->device_name) != 0 || strcmp(device_vendor, dinfo->device_vendor) != 0) { - printf("Device list is different to device list from opencl program (Device %d: '%s - %s' != '%s - %s')\n", i, device_vendor, device_name, dinfo->device_vendor, dinfo->device_name); - return (1); - } - if (nbits != dinfo->nbits) { - printf("Pointer size of device and stored device binary differs\n"); - return (1); - } - current_ptr += sizeof(_makefiles_opencl_device_info); - // printf("Device %d: %s %s (size %ld)\n", i, dinfo->device_vendor, dinfo->device_name, (int64_t) dinfo->binary_size); - program_sizes[i] = dinfo->binary_size; - program_binaries[i] = current_ptr; - current_ptr += dinfo->binary_size; - } - - cl_int return_status[pinfo->count]; - cl_int ocl_error; - *program = clCreateProgramWithBinary(context, num_devices, devices, program_sizes.data(), (const uint8_t**)program_binaries.data(), return_status, &ocl_error); - - if (ocl_error != CL_SUCCESS) { - printf("Error loading program\n"); - return (1); - } - - for (uint32_t i = 0; i < pinfo->count; i++) { - if (return_status[i] != CL_SUCCESS) { - printf("Error loading program for device %d\n", i); - clReleaseProgram(*program); - return (1); - } - } - - ocl_error = clBuildProgram(*program, num_devices, devices, "", nullptr, nullptr); - if (ocl_error != CL_SUCCESS) { - printf("Error building program\n"); - clReleaseProgram(*program); - return (1); - } - - return (0); -} - -#endif diff --git a/GPU/GPUTracking/utils/opencl_compiler_structs.h b/GPU/GPUTracking/utils/stdspinlock.h similarity index 54% rename from GPU/GPUTracking/utils/opencl_compiler_structs.h rename to GPU/GPUTracking/utils/stdspinlock.h index 68e0a4f184480..14bf95c45968e 100644 --- a/GPU/GPUTracking/utils/opencl_compiler_structs.h +++ b/GPU/GPUTracking/utils/stdspinlock.h @@ -9,20 +9,36 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file opencl_compiler_structs.h +/// \file stdspinlock.h /// \author David Rohr -struct _makefiles_opencl_platform_info { - char platform_profile[64]; - char platform_version[64]; - char platform_name[64]; - char platform_vendor[64]; - cl_uint count; -}; +#ifndef Q_STDSPINLOCK_H +#define Q_STDSPINLOCK_H + +#include -struct _makefiles_opencl_device_info { - char device_name[64]; - char device_vendor[64]; - cl_uint nbits; - size_t binary_size; +class stdspinlock +{ + public: + stdspinlock(std::atomic_flag& flag) : mFlag(&flag) + { + while (flag.test_and_set(std::memory_order_acquire)) { + } + } + void release() + { + if (mFlag) { + mFlag->clear(std::memory_order_release); + mFlag = nullptr; + } + } + ~stdspinlock() + { + release(); + } + + private: + std::atomic_flag* mFlag; }; + +#endif // Q_STDSPINLOCK_H diff --git a/GPU/GPUTracking/utils/strtag.h b/GPU/GPUTracking/utils/strtag.h index 24c527ca11a8c..a822083da8980 100644 --- a/GPU/GPUTracking/utils/strtag.h +++ b/GPU/GPUTracking/utils/strtag.h @@ -15,20 +15,21 @@ #ifndef STRTAG_H #define STRTAG_H -#include +#include +#include #include +#include -template -constexpr T qStr2Tag(const char* str) +template +constexpr T qStr2Tag(const char (&str)[N]) { - if (strlen(str) != sizeof(T)) { - throw std::runtime_error("Invalid tag length"); + static_assert(std::is_trivially_copyable_v); + static_assert(N - 1 == sizeof(T), "Invalid tag length"); + T value{}; + for (std::size_t i = 0; i < sizeof(T); ++i) { + value |= T(static_cast(str[i])) << (i * 8); } - T tmp; - for (uint32_t i = 0; i < sizeof(T); i++) { - ((char*)&tmp)[i] = str[i]; - } - return tmp; + return value; } template diff --git a/GPU/GPUbenchmark/cuda/Kernels.cu b/GPU/GPUbenchmark/cuda/Kernels.cu index 16dc138ae466f..b8dedfd8145b1 100644 --- a/GPU/GPUbenchmark/cuda/Kernels.cu +++ b/GPU/GPUbenchmark/cuda/Kernels.cu @@ -304,7 +304,6 @@ void printDeviceProp(int32_t deviceId) int32_t clockRateKHz = 0; int32_t memoryClockRateKHz = 0; int32_t computeMode = 0; - int32_t cooperativeMultiDevice = 0; #if (CUDART_VERSION >= 13000) GPUCHECK(cudaDeviceGetAttribute(&clockRateKHz, cudaDevAttrClockRate, deviceId)); @@ -314,7 +313,6 @@ void printDeviceProp(int32_t deviceId) clockRateKHz = props.clockRate; memoryClockRateKHz = props.memoryClockRate; computeMode = props.computeMode; - cooperativeMultiDevice = props.cooperativeMultiDeviceLaunch; #endif std::cout << std::setw(w1) << "Name: " << props.name << std::endl; std::cout << std::setw(w1) << "pciBusID: " << props.pciBusID << std::endl; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 14cd892b2554a..d9e35ba8bf405 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -194,6 +194,7 @@ class TPCFastTransform : public FlatObject /// Inverse transformation GPUd() void InverseTransformInTimeFrame(int32_t slice, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t slice, float z, float maxTimeBin) const; /// Inverse transformation: Transformed Y and Z -> transformed X GPUd() void InverseTransformYZtoX(int32_t slice, int32_t row, float y, float z, float& x, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; @@ -667,6 +668,13 @@ GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t slice, int32_ convUVtoPadTimeInTimeFrame(slice, row, u, v, pad, time, maxTimeBin); } +GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t slice, float z, float maxTimeBin) const +{ + float pad, time; + InverseTransformInTimeFrame(slice, 0, 0, 0, z, pad, time, maxTimeBin); + return time; +} + GPUdi() void TPCFastTransform::TransformIdealZ(int32_t slice, float time, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ diff --git a/GPU/Workflow/helper/include/GPUWorkflowHelper/GPUWorkflowHelper.h b/GPU/Workflow/helper/include/GPUWorkflowHelper/GPUWorkflowHelper.h index 225b6f75b1511..2802811b2e02b 100644 --- a/GPU/Workflow/helper/include/GPUWorkflowHelper/GPUWorkflowHelper.h +++ b/GPU/Workflow/helper/include/GPUWorkflowHelper/GPUWorkflowHelper.h @@ -14,7 +14,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsGlobalTracking/RecoContainer.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include namespace o2::gpu diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 4f62f07593bff..8dfbdaff7272f 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -85,6 +85,7 @@ struct TPCPadGainCalib; struct TPCZSLinkMapping; struct GPUSettingsO2; struct GPUSettingsProcessingNNclusterizer; +class GPUTRDRecoParam; class GPUO2InterfaceQA; struct GPUTrackingInOutPointers; struct GPUTrackingInOutZS; @@ -135,6 +136,11 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool tpcTriggerHandling = false; bool isITS3 = false; bool useFilteredOutputSpecs = false; + + // NN clusterizer + bool nnLoadFromCCDB = false; + bool nnDumpToFile = false; + std::vector nnEvalMode; }; GPURecoWorkflowSpec(CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, uint64_t tpcSectorMask, std::shared_ptr& ggr, std::function** gPolicyOrder = nullptr); @@ -207,6 +213,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task std::unique_ptr mTPCZSLinkMapping; std::unique_ptr mTPCVDriftHelper; std::unique_ptr mTRDGeometry; + std::unique_ptr mTRDRecoParam; std::unique_ptr mConfig; std::unique_ptr mConfParam; std::unique_ptr mTimer; @@ -225,11 +232,12 @@ class GPURecoWorkflowSpec : public o2::framework::Task int64_t mCreationForCalib = -1; ///< creation time for calib manipulation int32_t mVerbosity = 0; uint32_t mNTFs = 0; + uint32_t mNTFDumps = 0; uint32_t mNDebugDumps = 0; uint32_t mNextThreadIndex = 0; bool mUpdateGainMapCCDB = true; std::unique_ptr mTFSettings; - std::unique_ptr mNNClusterizerSettings; + std::map nnCCDBSettings; Config mSpecConfig; std::shared_ptr mGGR; @@ -239,6 +247,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool mMatLUTCreated = false; bool mITSGeometryCreated = false; bool mTRDGeometryCreated = false; + bool mTRDRecoParamCreated = false; bool mPropagatorInstanceCreated = false; int32_t mTPCCutAtTimeBin = -1; }; diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index fe55b83633972..b1c8d619ec736 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -13,6 +13,7 @@ /// @author David Rohr, Matteo Concas #include "GPUWorkflow/GPUWorkflowSpec.h" +#include "GPUO2Interface.h" #include "Framework/WorkflowSpec.h" // o2::framework::mergeInputs #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" diff --git a/GPU/Workflow/src/GPUWorkflowInternal.h b/GPU/Workflow/src/GPUWorkflowInternal.h index 7ac9c60048e20..1ad6f3df13f5a 100644 --- a/GPU/Workflow/src/GPUWorkflowInternal.h +++ b/GPU/Workflow/src/GPUWorkflowInternal.h @@ -15,7 +15,7 @@ #ifndef O2_GPU_GPUWORKFLOWINTERNAL_H #define O2_GPU_GPUWORKFLOWINTERNAL_H -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include #include #include @@ -47,6 +47,7 @@ struct GPURecoWorkflow_QueueObject { bool jobSubmitted = false; bool jobFinished = false; int32_t jobReturnValue = 0; + volatile int32_t jobThreadIndex = -1; std::mutex jobFinishedMutex; std::condition_variable jobFinishedNotify; bool jobInputFinal = false; diff --git a/GPU/Workflow/src/GPUWorkflowPipeline.cxx b/GPU/Workflow/src/GPUWorkflowPipeline.cxx index 8867b6c336f97..f0aeb8089e27a 100644 --- a/GPU/Workflow/src/GPUWorkflowPipeline.cxx +++ b/GPU/Workflow/src/GPUWorkflowPipeline.cxx @@ -15,7 +15,7 @@ #include "GPUWorkflow/GPUWorkflowSpec.h" #include "GPUO2InterfaceConfiguration.h" #include "GPUO2Interface.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUSettings.h" #include "GPUWorkflowInternal.h" @@ -90,6 +90,7 @@ void GPURecoWorkflowSpec::RunWorkerThread(int32_t id) context = workerContext.inputQueue.front(); workerContext.inputQueue.pop(); } + context->jobThreadIndex = id; context->jobReturnValue = runMain(nullptr, context->jobPtrs, context->jobOutputRegions, id, context->jobInputUpdateCallback.get()); { std::lock_guard lk(context->jobFinishedMutex); @@ -179,8 +180,7 @@ int32_t GPURecoWorkflowSpec::handlePipeline(ProcessingContext& pc, GPUTrackingIn } mPipeline->completionPolicyQueue.pop(); } - } - if (mSpecConfig.enableDoublePipeline == 2) { + } else if (mSpecConfig.enableDoublePipeline == 2) { auto prepareDummyMessage = pc.outputs().make>(Output{gDataOriginGPU, "PIPELINEPREPARE", 0}, 0u); size_t ptrsTotal = 0; diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 6c76f13c9bbd0..a8f95841a4dc9 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -54,17 +54,18 @@ #include "GPUO2Interface.h" #include "GPUO2InterfaceUtils.h" #include "CalibdEdxContainer.h" +#include "ORTRootSerializer.h" #include "GPUNewCalibValues.h" #include "TPCPadGainCalib.h" #include "TPCZSLinkMapping.h" #include "display/GPUDisplayInterface.h" #include "TPCBase/Sector.h" #include "TPCBase/Utils.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCCalibration/VDriftHelper.h" #include "CorrectionMapsHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" -#include "TPCBase/DeadChannelMapCreator.h" +#include "TPCBaseRecSim/DeadChannelMapCreator.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "Algorithm/Parser.h" @@ -72,13 +73,14 @@ #include "DataFormatsTRD/RecoInputContainer.h" #include "TRDBase/Geometry.h" #include "TRDBase/GeometryFlat.h" +#include "GPUTRDRecoParam.h" #include "ITSBase/GeometryTGeo.h" #include "CommonUtils/DebugStreamer.h" #include "GPUReconstructionConvert.h" #include "DetectorsRaw/RDHUtils.h" #include "ITStracking/TrackingInterface.h" #include "GPUWorkflowInternal.h" -#include "TPCCalibration/NeuralNetworkClusterizer.h" +#include "GPUDataTypesQA.h" // #include "Framework/ThreadPool.h" #include @@ -133,50 +135,6 @@ void GPURecoWorkflowSpec::init(InitContext& ic) { GRPGeomHelper::instance().setRequest(mGGR); GPUO2InterfaceConfiguration& config = *mConfig.get(); - GPUSettingsProcessingNNclusterizer& mNNClusterizerSettings = mConfig->configProcessing.nn; - - if (mNNClusterizerSettings.nnLoadFromCCDB) { - LOG(info) << "Loading neural networks from CCDB"; - o2::tpc::NeuralNetworkClusterizer nnClusterizerFetcher; - nnClusterizerFetcher.initCcdbApi(mNNClusterizerSettings.nnCCDBURL); - std::map ccdbSettings = { - {"nnCCDBURL", mNNClusterizerSettings.nnCCDBURL}, - {"nnCCDBPath", mNNClusterizerSettings.nnCCDBPath}, - {"inputDType", mNNClusterizerSettings.nnInferenceInputDType}, - {"outputDType", mNNClusterizerSettings.nnInferenceOutputDType}, - {"outputFolder", mNNClusterizerSettings.nnLocalFolder}, - {"nnCCDBPath", mNNClusterizerSettings.nnCCDBPath}, - {"nnCCDBWithMomentum", std::to_string(mNNClusterizerSettings.nnCCDBWithMomentum)}, - {"nnCCDBBeamType", mNNClusterizerSettings.nnCCDBBeamType}, - {"nnCCDBInteractionRate", std::to_string(mNNClusterizerSettings.nnCCDBInteractionRate)}}; - - std::string nnFetchFolder = mNNClusterizerSettings.nnLocalFolder; - std::vector evalMode = o2::utils::Str::tokenize(mNNClusterizerSettings.nnEvalMode, ':'); - - if (evalMode[0] == "c1") { - ccdbSettings["nnCCDBLayerType"] = mNNClusterizerSettings.nnCCDBClassificationLayerType; - ccdbSettings["nnCCDBEvalType"] = "classification_c1"; - ccdbSettings["outputFile"] = "net_classification_c1.onnx"; - nnClusterizerFetcher.loadIndividualFromCCDB(ccdbSettings); - } else if (evalMode[0] == "c2") { - ccdbSettings["nnCCDBLayerType"] = mNNClusterizerSettings.nnCCDBClassificationLayerType; - ccdbSettings["nnCCDBEvalType"] = "classification_c2"; - ccdbSettings["outputFile"] = "net_classification_c2.onnx"; - nnClusterizerFetcher.loadIndividualFromCCDB(ccdbSettings); - } - - ccdbSettings["nnCCDBLayerType"] = mNNClusterizerSettings.nnCCDBRegressionLayerType; - ccdbSettings["nnCCDBEvalType"] = "regression_c1"; - ccdbSettings["outputFile"] = "net_regression_c1.onnx"; - nnClusterizerFetcher.loadIndividualFromCCDB(ccdbSettings); - if (evalMode[1] == "r2") { - ccdbSettings["nnCCDBLayerType"] = mNNClusterizerSettings.nnCCDBRegressionLayerType; - ccdbSettings["nnCCDBEvalType"] = "regression_c2"; - ccdbSettings["outputFile"] = "net_regression_c2.onnx"; - nnClusterizerFetcher.loadIndividualFromCCDB(ccdbSettings); - } - LOG(info) << "Neural network loading done!"; - } // Create configuration object and fill settings mConfig->configGRP.solenoidBzNominalGPU = 0; @@ -185,6 +143,7 @@ void GPURecoWorkflowSpec::init(InitContext& ic) mTFSettings->simStartOrbit = hbfu.getFirstIRofTF(o2::InteractionRecord(0, hbfu.orbitFirstSampled)).orbit; *mConfParam = mConfig->ReadConfigurableParam(); + if (mConfParam->display) { mDisplayFrontend.reset(GPUDisplayFrontendInterface::getFrontend(mConfig->configDisplay.displayFrontend.c_str())); mConfig->configProcessing.eventDisplay = mDisplayFrontend.get(); @@ -223,54 +182,50 @@ void GPURecoWorkflowSpec::init(InitContext& ic) mConfig->configQA.shipToQC = true; if (!mConfig->configProcessing.runQA) { mConfig->configQA.enableLocalOutput = false; - mQATaskMask = (mSpecConfig.processMC ? 15 : 0) | (mConfig->configQA.clusterRejectionHistograms ? 32 : 0); + mQATaskMask = (mSpecConfig.processMC ? gpudatatypes::gpuqa::tasksAllMC : gpudatatypes::gpuqa::tasksNone) | (mConfig->configQA.clusterRejectionHistograms ? gpudatatypes::gpuqa::taskClusterCounts : gpudatatypes::gpuqa::tasksNone); mConfig->configProcessing.runQA = -mQATaskMask; } } mConfig->configInterface.outputToExternalBuffers = true; - if (mConfParam->synchronousProcessing) { - mConfig->configReconstruction.useMatLUT = false; - } - if (mConfig->configProcessing.rtc.optSpecialCode == -1) { - mConfig->configProcessing.rtc.optSpecialCode = mConfParam->synchronousProcessing; - } + const bool runTracking = mSpecConfig.outputTracks || mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat; // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) - if (mSpecConfig.outputTracks || mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat) { - mConfig->configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, - GPUDataTypes::RecoStep::TPCSectorTracking, - GPUDataTypes::RecoStep::TPCMerging); - mConfig->configWorkflow.outputs.set(GPUDataTypes::InOutType::TPCMergedTracks); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, mConfParam->rundEdx == -1 ? !mConfParam->synchronousProcessing : mConfParam->rundEdx); + if (runTracking) { + mConfig->configWorkflow.steps.set(gpudatatypes::RecoStep::TPCConversion, + gpudatatypes::RecoStep::TPCSectorTracking, + gpudatatypes::RecoStep::TPCMerging); + mConfig->configWorkflow.outputs.set(gpudatatypes::InOutType::TPCMergedTracks); } + GPUO2Interface::ApplySyncSettings(mConfig->configProcessing, mConfig->configReconstruction, mConfig->configWorkflow.steps, mConfParam->synchronousProcessing, runTracking ? mConfParam->rundEdx : -2); + if (mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat) { - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, true); + mConfig->configWorkflow.steps.setBits(gpudatatypes::RecoStep::TPCCompression, true); + mConfig->configWorkflow.outputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, true); } - mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCClusters); + mConfig->configWorkflow.inputs.set(gpudatatypes::InOutType::TPCClusters); if (mSpecConfig.caClusterer) { // Override some settings if we have raw data as input - mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCRaw); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, true); + mConfig->configWorkflow.inputs.set(gpudatatypes::InOutType::TPCRaw); + mConfig->configWorkflow.steps.setBits(gpudatatypes::RecoStep::TPCClusterFinding, true); + mConfig->configWorkflow.outputs.setBits(gpudatatypes::InOutType::TPCClusters, true); } if (mSpecConfig.decompressTPC) { - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCDecompression, true); - mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCCompressedClusters); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, false); + mConfig->configWorkflow.steps.setBits(gpudatatypes::RecoStep::TPCCompression, false); + mConfig->configWorkflow.steps.setBits(gpudatatypes::RecoStep::TPCDecompression, true); + mConfig->configWorkflow.inputs.set(gpudatatypes::InOutType::TPCCompressedClusters); + mConfig->configWorkflow.outputs.setBits(gpudatatypes::InOutType::TPCClusters, true); + mConfig->configWorkflow.outputs.setBits(gpudatatypes::InOutType::TPCCompressedClusters, false); if (mTPCSectorMask != 0xFFFFFFFFF) { throw std::invalid_argument("Cannot run TPC decompression with a sector mask"); } } if (mSpecConfig.runTRDTracking) { - mConfig->configWorkflow.inputs.setBits(GPUDataTypes::InOutType::TRDTracklets, true); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, true); + mConfig->configWorkflow.inputs.setBits(gpudatatypes::InOutType::TRDTracklets, true); + mConfig->configWorkflow.steps.setBits(gpudatatypes::RecoStep::TRDTracking, true); } if (mSpecConfig.runITSTracking) { - mConfig->configWorkflow.inputs.setBits(GPUDataTypes::InOutType::ITSClusters, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::ITSTracks, true); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::ITSTracking, true); + mConfig->configWorkflow.inputs.setBits(gpudatatypes::InOutType::ITSClusters, true); + mConfig->configWorkflow.outputs.setBits(gpudatatypes::InOutType::ITSTracks, true); + mConfig->configWorkflow.steps.setBits(gpudatatypes::RecoStep::ITSTracking, true); } if (mSpecConfig.outputSharedClusterMap) { mConfig->configProcessing.outputSharedClusterMap = true; @@ -319,6 +274,9 @@ void GPURecoWorkflowSpec::init(InitContext& ic) if (mSpecConfig.readTRDtracklets) { mTRDGeometry = std::make_unique(); mConfig->configCalib.trdGeometry = mTRDGeometry.get(); + + mTRDRecoParam = std::make_unique(); + mConfig->configCalib.trdRecoParam = mTRDRecoParam.get(); } mConfig->configProcessing.willProvideO2PropagatorLate = true; @@ -825,11 +783,31 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) lockDecodeInput.reset(); + uint32_t threadIndex; if (mConfParam->dump) { - if (mNTFs == 1) { - mGPUReco->DumpSettings(); + if (mSpecConfig.enableDoublePipeline && pipelineContext->jobSubmitted) { + while (pipelineContext->jobThreadIndex == -1) { + } + threadIndex = pipelineContext->jobThreadIndex; + } else { + threadIndex = 0; // TODO: Not sure if this is safe, but it is not yet known which threadIndex will pick up the enqueued job + } + + std::string dir = ""; + if (mConfParam->dumpFolder != "") { + dir = std::regex_replace(mConfParam->dumpFolder, std::regex("\\[P\\]"), std::to_string(getpid())); + if (mNTFs == 1) { + mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + } + dir += "/"; + } + if (mNTFs == 1) { // Must dump with first TF, since will enforce enqueued calib updates + mGPUReco->DumpSettings(threadIndex, dir.c_str()); + } + if (tinfo.tfCounter >= mConfParam->dumpFirst && (mConfParam->dumpLast == -1 || tinfo.tfCounter <= mConfParam->dumpLast)) { + mGPUReco->DumpEvent(mNTFDumps, &ptrs, threadIndex, dir.c_str()); + mNTFDumps++; } - mGPUReco->DumpEvent(mNTFs - 1, &ptrs); } std::unique_ptr ptrsDump; if (mConfParam->dumpBadTFMode == 2) { @@ -847,9 +825,10 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) std::unique_lock lk(pipelineContext->jobFinishedMutex); pipelineContext->jobFinishedNotify.wait(lk, [context = pipelineContext.get()]() { return context->jobFinished; }); retVal = pipelineContext->jobReturnValue; + threadIndex = pipelineContext->jobThreadIndex; } else { // uint32_t threadIndex = pc.services().get().threadIndex; - uint32_t threadIndex = mNextThreadIndex; + threadIndex = mNextThreadIndex; if (mConfig->configProcessing.doublePipeline) { mNextThreadIndex = (mNextThreadIndex + 1) % 2; } @@ -879,7 +858,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } fclose(fp); } else if (mConfParam->dumpBadTFMode == 2) { - mGPUReco->DumpEvent(mNDebugDumps - 1, ptrsDump.get()); + mGPUReco->DumpEvent(mNDebugDumps - 1, ptrsDump.get(), threadIndex); } } @@ -962,7 +941,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } } - if (mConfig->configWorkflow.outputs.isSet(GPUDataTypes::InOutType::TPCMergedTracks)) { + if (mConfig->configWorkflow.outputs.isSet(gpudatatypes::InOutType::TPCMergedTracks)) { LOG(info) << "found " << ptrs.nOutputTracksTPCO2 << " track(s)"; } @@ -1084,14 +1063,21 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c } mMatLUTCreated = true; } - if (mSpecConfig.readTRDtracklets && !mTRDGeometryCreated) { - auto gm = o2::trd::Geometry::instance(); - gm->createPadPlaneArray(); - gm->createClusterMatrixArray(); - mTRDGeometry = std::make_unique(*gm); - newCalibObjects.trdGeometry = mConfig->configCalib.trdGeometry = mTRDGeometry.get(); - LOG(info) << "Loaded TRD geometry"; - mTRDGeometryCreated = true; + if (mSpecConfig.readTRDtracklets) { + if (!mTRDGeometryCreated) { + auto gm = o2::trd::Geometry::instance(); + gm->createPadPlaneArray(); + gm->createClusterMatrixArray(); + mTRDGeometry = std::make_unique(*gm); + newCalibObjects.trdGeometry = mConfig->configCalib.trdGeometry = mTRDGeometry.get(); + LOG(info) << "Loaded TRD geometry"; + mTRDGeometryCreated = true; + } + if (!mTRDRecoParamCreated) { + mTRDRecoParam = std::make_unique(); + newCalibObjects.trdRecoParam = mConfig->configCalib.trdRecoParam = mTRDRecoParam.get(); + mTRDRecoParamCreated = true; + } } } needCalibUpdate = fetchCalibsCCDBTPC(pc, newCalibObjects, oldCalibObjects) || needCalibUpdate; @@ -1103,6 +1089,27 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c newCalibValues.tpcTimeBinCut = mConfig->configGRP.tpcCutTimeBin = mTPCCutAtTimeBin; needCalibUpdate = true; } + if (mSpecConfig.nnLoadFromCCDB) { + auto dumpToFile = [](const char* buffer, std::size_t validSize, const std::string& path) { + std::ofstream out(path, std::ios::binary | std::ios::trunc); + if (!out.is_open()) { + throw std::runtime_error("Failed to open output file: " + path); + } + + out.write(buffer, static_cast(validSize)); + if (!out) { + throw std::runtime_error("Failed while writing data to: " + path); + } + }; + for (int i = 0; i < 3; i++) { + newCalibObjects.nnClusterizerNetworks[i] = mConfig->configCalib.nnClusterizerNetworks[i]; + if (mSpecConfig.nnDumpToFile && newCalibObjects.nnClusterizerNetworks[i]) { + std::string path = "tpc_nn_clusterizer_" + std::to_string(i) + ".onnx"; + dumpToFile(newCalibObjects.nnClusterizerNetworks[i]->getONNXModel(), newCalibObjects.nnClusterizerNetworks[i]->getONNXModelSize(), path); + LOG(info) << "Dumped TPC clusterizer NN " << i << " to file " << path; + } + } + } if (needCalibUpdate) { LOG(info) << "Updating GPUReconstruction calibration objects"; mGPUReco->UpdateCalibration(newCalibObjects, newCalibValues); @@ -1241,6 +1248,67 @@ Inputs GPURecoWorkflowSpec::inputs() } } + // NN clusterizer + *mConfParam = mConfig->ReadConfigurableParam(); + if (mConfig->configProcessing.nn.nnLoadFromCCDB) { + + LOG(info) << "(NN CLUS) Enabling fetching of TPC NN clusterizer from CCDB"; + mSpecConfig.nnLoadFromCCDB = true; + mSpecConfig.nnDumpToFile = mConfig->configProcessing.nn.nnCCDBDumpToFile; + GPUSettingsProcessingNNclusterizer& nnClusterizerSettings = mConfig->configProcessing.nn; + + std::map metadata; + metadata["inputDType"] = nnClusterizerSettings.nnInferenceInputDType; // FP16 or FP32 + metadata["outputDType"] = nnClusterizerSettings.nnInferenceOutputDType; // FP16 or FP32 + metadata["nnCCDBWithMomentum"] = nnClusterizerSettings.nnCCDBWithMomentum; // 0, 1 -> Only for regression model + metadata["nnCCDBLayerType"] = nnClusterizerSettings.nnCCDBClassificationLayerType; // FC, CNN + metadata["nnCCDBInteractionRate"] = nnClusterizerSettings.nnCCDBInteractionRate; // in kHz + metadata["nnCCDBBeamType"] = nnClusterizerSettings.nnCCDBBeamType; // pp, pPb, PbPb + + auto convert_map_to_metadata = [](const std::map& inputMap, std::vector& outputMetadata) { + for (const auto& [key, value] : inputMap) { + if (value != "") { + outputMetadata.push_back({key, value}); + } + } + }; + + mSpecConfig.nnEvalMode = o2::utils::Str::tokenize(nnClusterizerSettings.nnEvalMode, ':'); + std::vector ccdb_metadata; + + if (mConfParam->printSettings) { + auto printSettings = [](const std::map& settings) { + LOG(info) << "(NN CLUS) NN Clusterizer CCDB settings:"; + for (const auto& [key, value] : settings) { + LOG(info) << " " << key << " : " << value; + } + }; + printSettings(metadata); + } + + if (mSpecConfig.nnEvalMode[0] == "c1") { + metadata["nnCCDBEvalType"] = "classification_c1"; + convert_map_to_metadata(metadata, ccdb_metadata); + inputs.emplace_back("nn_classification_c1", gDataOriginTPC, "NNCLUSTERIZER_C1", 0, Lifetime::Condition, ccdbParamSpec(nnClusterizerSettings.nnCCDBPath + "/" + metadata["nnCCDBEvalType"], ccdb_metadata, 0)); + } else if (mSpecConfig.nnEvalMode[0] == "c2") { + metadata["nnCCDBLayerType"] = nnClusterizerSettings.nnCCDBRegressionLayerType; + metadata["nnCCDBEvalType"] = "classification_c2"; + convert_map_to_metadata(metadata, ccdb_metadata); + inputs.emplace_back("nn_classification_c2", gDataOriginTPC, "NNCLUSTERIZER_C2", 0, Lifetime::Condition, ccdbParamSpec(nnClusterizerSettings.nnCCDBPath + "/" + metadata["nnCCDBEvalType"], ccdb_metadata, 0)); + } + + metadata["nnCCDBEvalType"] = "regression_c1"; + metadata["nnCCDBLayerType"] = nnClusterizerSettings.nnCCDBRegressionLayerType; + convert_map_to_metadata(metadata, ccdb_metadata); + inputs.emplace_back("nn_regression_c1", gDataOriginTPC, "NNCLUSTERIZER_R1", 0, Lifetime::Condition, ccdbParamSpec(nnClusterizerSettings.nnCCDBPath + "/" + metadata["nnCCDBEvalType"], ccdb_metadata, 0)); + + if (mSpecConfig.nnEvalMode[1] == "r2") { + metadata["nnCCDBEvalType"] = "regression_c2"; + convert_map_to_metadata(metadata, ccdb_metadata); + inputs.emplace_back("nn_regression_c2", gDataOriginTPC, "NNCLUSTERIZER_R2", 0, Lifetime::Condition, ccdbParamSpec(nnClusterizerSettings.nnCCDBPath + "/" + metadata["nnCCDBEvalType"], ccdb_metadata, 0)); + } + } + return inputs; }; diff --git a/GPU/Workflow/src/GPUWorkflowTPC.cxx b/GPU/Workflow/src/GPUWorkflowTPC.cxx index 6606386819b64..2b2f81246fc04 100644 --- a/GPU/Workflow/src/GPUWorkflowTPC.cxx +++ b/GPU/Workflow/src/GPUWorkflowTPC.cxx @@ -49,18 +49,19 @@ #include "GPUO2Interface.h" #include "GPUO2InterfaceUtils.h" #include "CalibdEdxContainer.h" +#include "ORTRootSerializer.h" #include "GPUNewCalibValues.h" #include "TPCPadGainCalib.h" #include "TPCZSLinkMapping.h" #include "display/GPUDisplayInterface.h" #include "TPCBase/Sector.h" #include "TPCBase/Utils.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCCalibration/VDriftHelper.h" #include "CorrectionMapsHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCCalibration/IDCContainer.h" -#include "TPCBase/DeadChannelMapCreator.h" +#include "TPCBaseRecSim/DeadChannelMapCreator.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "Algorithm/Parser.h" @@ -293,6 +294,18 @@ void GPURecoWorkflowSpec::finaliseCCDBTPC(ConcreteDataMatcher& matcher, void* ob mTPCDeadChannelMapCreator->getDeadChannelMapFEE().getSum(), mTPCDeadChannelMapCreator->getDeadChannelMap().getSum()); } else if (mTPCVDriftHelper->accountCCDBInputs(matcher, obj)) { } else if (mCalibObjects.mFastTransformHelper->accountCCDBInputs(matcher, obj)) { + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "NNCLUSTERIZER_C1", 0)) { + mConfig->configCalib.nnClusterizerNetworks[0] = static_cast(obj); + LOG(info) << "(NN CLUS) " << (mConfig->configCalib.nnClusterizerNetworks[0])->getONNXModelSize() << " bytes loaded for NN clusterizer: classification_c1"; + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "NNCLUSTERIZER_C2", 0)) { + mConfig->configCalib.nnClusterizerNetworks[0] = static_cast(obj); + LOG(info) << "(NN CLUS) " << (mConfig->configCalib.nnClusterizerNetworks[0])->getONNXModelSize() << " bytes loaded for NN clusterizer: classification_c2"; + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "NNCLUSTERIZER_R1", 0)) { + mConfig->configCalib.nnClusterizerNetworks[1] = static_cast(obj); + LOG(info) << "(NN CLUS) " << (mConfig->configCalib.nnClusterizerNetworks[1])->getONNXModelSize() << " bytes loaded for NN clusterizer: regression_c1"; + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "NNCLUSTERIZER_R2", 0)) { + mConfig->configCalib.nnClusterizerNetworks[2] = static_cast(obj); + LOG(info) << "(NN CLUS) " << (mConfig->configCalib.nnClusterizerNetworks[2])->getONNXModelSize() << " bytes loaded for NN clusterizer: regression_c2"; } } @@ -405,6 +418,21 @@ bool GPURecoWorkflowSpec::fetchCalibsCCDBTPC(ProcessingCon newCalibObjects.tpcPadGain = mCalibObjects.mTPCPadGainCalib.get(); mustUpdate = true; } + + // NN clusterizer networks + if (mSpecConfig.nnLoadFromCCDB) { + + if (mSpecConfig.nnEvalMode[0] == "c1") { + pc.inputs().get("nn_classification_c1"); + } else if (mSpecConfig.nnEvalMode[0] == "c2") { + pc.inputs().get("nn_classification_c2"); + } + + pc.inputs().get("nn_regression_c1"); + if (mSpecConfig.nnEvalMode[1] == "r2") { + pc.inputs().get("nn_regression_c2"); + } + } } return mustUpdate; } diff --git a/GPU/documentation/build-O2.md b/GPU/documentation/build-O2.md index dd21f7e154a63..b04fe562b8c2f 100644 --- a/GPU/documentation/build-O2.md +++ b/GPU/documentation/build-O2.md @@ -37,7 +37,7 @@ Advantages: - One can see enabled GPU features / versions / architectures in the version string of `gpu-system`. Disadvantages: -- Need system `CMake` >= `3.26` for the detsction at aliBuild level. +- Need system `CMake` >= `3.26` for the detection at aliBuild level. - `FindO2GPU.cmake` is duplicated in O2 and alidist and must be kept in sync. But at least this is checked and gives an error otherwise. - Running cmake during the system check takes around 5 sec for every aliBuild command involving O2 or ONNX. diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 02caa63df0d43..287536ff118f7 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -41,6 +41,8 @@ o2_add_library(Generators src/GeneratorTParticleParam.cxx src/GeneratorService.cxx src/FlowMapper.cxx + src/TPCLoopers.cxx + src/TPCLoopersParam.cxx $<$:src/GeneratorPythia8.cxx> $<$:src/DecayerPythia8.cxx> $<$:src/GeneratorPythia8Param.cxx> @@ -53,6 +55,7 @@ o2_add_library(Generators PUBLIC_LINK_LIBRARIES FairRoot::Base O2::SimConfig O2::CommonUtils O2::DetectorsBase O2::ZDCBase O2::SimulationDataFormat ${pythiaTarget} ${hepmcTarget} FairRoot::Gen + onnxruntime::onnxruntime TARGETVARNAME targetName) if(pythia_FOUND) @@ -63,6 +66,8 @@ if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() +target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) + set(headers include/Generators/Generator.h include/Generators/Trigger.h @@ -88,6 +93,10 @@ set(headers include/Generators/FlowMapper.h ) +list(APPEND headers + include/Generators/TPCLoopers.h + include/Generators/TPCLoopersParam.h) + if(pythia_FOUND) list(APPEND headers include/Generators/GeneratorPythia8.h @@ -158,4 +167,5 @@ endif() o2_data_file(COPY share/external DESTINATION Generators) o2_data_file(COPY share/egconfig DESTINATION Generators) +o2_data_file(COPY share/TPCLoopers DESTINATION Generators) o2_data_file(COPY share/pythia8 DESTINATION Generators) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index bd35a00793e2d..f413aeccfa3ab 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -34,6 +34,8 @@ namespace o2 namespace eventgen { +class GenTPCLoopers; // Forward declaration + /*****************************************************************/ /*****************************************************************/ @@ -56,7 +58,7 @@ class Generator : public FairGenerator /** constructor **/ Generator(const Char_t* name, const Char_t* title = "ALICEo2 Generator"); /** destructor **/ - ~Generator() override = default; + ~Generator() override; /** Initialize the generator if needed **/ Bool_t Init() override; @@ -73,6 +75,7 @@ class Generator : public FairGenerator /** methods to override **/ virtual Bool_t generateEvent() = 0; // generates event (in structure internal to generator) virtual Bool_t importParticles() = 0; // fills the mParticles vector (transfer from generator state) + Bool_t finalizeEvent(); // final part of event generation that can be customised using external macros virtual void updateHeader(o2::dataformats::MCEventHeader* eventHeader) {}; Bool_t triggerEvent(); @@ -154,6 +157,8 @@ class Generator : public FairGenerator private: void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const; + // loopers flag + Bool_t mAddTPCLoopers = kFALSE; // Flag is automatically set to true if TPC is in readout detectors, loopers are not vetoed and transport is enabled // collect an ID and a short description of sub-generator entities std::unordered_map mSubGeneratorsIdToDesc; // the current ID of the sub-generator used in the current event (if applicable) @@ -162,6 +167,12 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; + // Loopers generator instance + o2::eventgen::GenTPCLoopers* mTPCLoopersGen = nullptr; +#ifdef GENERATORS_WITH_TPCLOOPERS + bool initTPCLoopersGen(); +#endif + ClassDefOverride(Generator, 2); }; /** class Generator **/ diff --git a/Generators/include/Generators/GeneratorHybrid.h b/Generators/include/Generators/GeneratorHybrid.h index b92437b02d874..97422df1c8e2d 100644 --- a/Generators/include/Generators/GeneratorHybrid.h +++ b/Generators/include/Generators/GeneratorHybrid.h @@ -54,8 +54,11 @@ class GeneratorHybrid : public Generator { public: - GeneratorHybrid(const std::string& inputgens); - ~GeneratorHybrid(); + GeneratorHybrid& operator=(const GeneratorHybrid&) = delete; + GeneratorHybrid(const GeneratorHybrid&) = delete; + + // Singleton access method + static GeneratorHybrid& Instance(const std::string& inputgens = ""); Bool_t Init() override; Bool_t generateEvent() override; @@ -66,8 +69,11 @@ class GeneratorHybrid : public Generator Bool_t confSetter(const auto& gen); template std::string jsonValueToString(const T& value); + std::vector> const& getGenerators() { return gens; } private: + GeneratorHybrid(const std::string& inputgens); + ~GeneratorHybrid(); o2::eventgen::Generator* currentgen = nullptr; std::vector> gens; const std::vector generatorNames = {"extkinO2", "evtpool", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"}; diff --git a/Generators/include/Generators/GeneratorHybridParam.h b/Generators/include/Generators/GeneratorHybridParam.h index c05b70dcb40ba..acdf87bba2d0a 100644 --- a/Generators/include/Generators/GeneratorHybridParam.h +++ b/Generators/include/Generators/GeneratorHybridParam.h @@ -29,9 +29,10 @@ namespace eventgen **/ struct GeneratorHybridParam : public o2::conf::ConfigurableParamHelper { - std::string configFile = ""; // JSON configuration file for the generators - bool randomize = false; // randomize the order of the generators, if not generator using fractions - int num_workers = 1; // number of threads available for asyn/parallel event generation + std::string configFile = ""; // JSON configuration file for the generators + bool randomize = false; // randomize the order of the generators, if not generator using fractions + int num_workers = 1; // number of threads available for asyn/parallel event generation + bool switchExtToHybrid = false; // force external generator to be executed as hybrid mode, useful for Hyperloop MCGEN O2ParamDef(GeneratorHybridParam, "GeneratorHybrid"); }; diff --git a/Generators/include/Generators/PrimaryGenerator.h b/Generators/include/Generators/PrimaryGenerator.h index 097bfabf4ef57..cd4d842761418 100644 --- a/Generators/include/Generators/PrimaryGenerator.h +++ b/Generators/include/Generators/PrimaryGenerator.h @@ -80,6 +80,9 @@ class PrimaryGenerator : public FairPrimaryGenerator /** Public embedding methods **/ Bool_t embedInto(TString fname); + /// sets the embedding index + void setEmbedIndex(int idx) { mEmbedIndex = idx; } + void setExternalVertexForNextEvent(double x, double y, double z); // sets the vertex mode; if mode is kCCDB, a valid MeanVertexObject pointer must be given at the same time diff --git a/Generators/include/Generators/TPCLoopers.h b/Generators/include/Generators/TPCLoopers.h new file mode 100644 index 0000000000000..a144a947fc11b --- /dev/null +++ b/Generators/include/Generators/TPCLoopers.h @@ -0,0 +1,146 @@ +// Copyright 2024-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. + +/// \author M+Giacalone - September 2025 + +#ifndef ALICEO2_EVENTGEN_TPCLOOPERS_H_ +#define ALICEO2_EVENTGEN_TPCLOOPERS_H_ + +#ifdef GENERATORS_WITH_TPCLOOPERS +#include +#include +#include +#include "TRandom3.h" +#include +#include "TParticle.h" + +// Static Ort::Env instance for multiple onnx model loading +extern Ort::Env global_env; + +// This class is responsible for loading the scaler parameters from a JSON file +// and applying the inverse transformation to the generated data. +// Inferenced output is scaled (min-max normalization or robust scaling for outlier features) during training, +// so we need to revert this transformation to get physical values. +struct Scaler { + std::vector normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector outlier_scale; + + void load(const std::string& filename); + + std::vector inverse_transform(const std::vector& input); + + private: + std::vector jsonArrayToVector(const rapidjson::Value& jsonArray); +}; + +// This class loads the ONNX model and generates samples using it. +class ONNXGenerator +{ + public: + ONNXGenerator(Ort::Env& shared_env, const std::string& model_path); + + std::vector generate_sample(); + + private: + Ort::Env& env; + Ort::Session session; + TRandom3 rand_gen; +}; +#endif // GENERATORS_WITH_TPCLOOPERS + +namespace o2 +{ +namespace eventgen +{ + +#ifdef GENERATORS_WITH_TPCLOOPERS +/** + * Generator for TPC Loopers based on pre-trained ONNX models. + * Currently it generates loopers as electron-positron pairs and Compton electrons + * according to specified distributions and parameters. + * This can be extended to other types of background processes in the future (e.g. slow neutron spallation products, saturation tail). + * Multiple configuration options are available: + * - Flat gas: loopers are generated uniformly per event taking a reference value which can be either the LHC orbit time or the average interaction time record interval from the collision context. + * ==> Current automatic setup (default) sets the interaction rate automatically from the collision context and the reference value per orbit is calculated from an external file. + * ==> Number of loopers per orbit can be adjusted via a specific parameter. + * - Poisson + Gaussian sampling: number of loopers are sampled from Poissonian (for pairs) and Gaussian (for Compton electrons) distributions based on provided parameters. + * ==> flat gas must be disabled to use this option. + * - Fixed number of loopers per event + * ==> flat gas must be disabled to use this option and Poissonian/Gaussian parameters file should be set to None + */ +class GenTPCLoopers +{ + public: + GenTPCLoopers(std::string model_pairs = "tpcloopmodel.onnx", std::string model_compton = "tpcloopmodelcompton.onnx", + std::string poisson = "poisson.csv", std::string gauss = "gauss.csv", std::string scaler_pair = "scaler_pair.json", + std::string scaler_compton = "scaler_compton.json"); + + Bool_t generateEvent(); + + Bool_t generateEvent(double time_limit); + + std::vector importParticles(); + + unsigned int PoissonPairs(); + + unsigned int GaussianElectrons(); + + void SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton); + + void SetMultiplier(const std::array& mult); + + void setFlatGas(Bool_t flat, Int_t number = -1, Int_t nloopers_orbit = -1); + + void setFractionPairs(float fractionPairs); + + void SetRate(const std::string& rateFile, bool isPbPb, int intRate = 50000); + + void SetAdjust(float adjust = 0.f); + + unsigned int getNLoopers() const { return (mNLoopersPairs + mNLoopersCompton); } + + private: + std::unique_ptr mONNX_pair = nullptr; + std::unique_ptr mONNX_compton = nullptr; + std::unique_ptr mScaler_pair = nullptr; + std::unique_ptr mScaler_compton = nullptr; + double mPoisson[3] = {0.0, 0.0, 0.0}; // Mu, Min and Max of Poissonian + double mGauss[4] = {0.0, 0.0, 0.0, 0.0}; // Mean, Std, Min, Max + std::vector> mGenPairs; + std::vector> mGenElectrons; + unsigned int mNLoopersPairs = -1; + unsigned int mNLoopersCompton = -1; + std::array mMultiplier = {1., 1.}; + bool mPoissonSet = false; + bool mGaussSet = false; + // Random number generator + TRandom3 mRandGen; + int mCurrentEvent = 0; // Current event number, used for adaptive loopers + TFile* mContextFile = nullptr; // Input collision context file + o2::steer::DigitizationContext* mCollisionContext = nullptr; // Pointer to the digitization context + std::vector mInteractionTimeRecords; // Interaction time records from collision context + Bool_t mFlatGas = false; // Flag to indicate if flat gas loopers are used + Bool_t mFlatGasOrbit = false; // Flag to indicate if flat gas loopers are per orbit + Int_t mFlatGasNumber = -1; // Number of flat gas loopers per event + double mIntTimeRecMean = 1.0; // Average interaction time record used for the reference + double mTimeLimit = 0.0; // Time limit for the current event + double mTimeEnd = 0.0; // Time limit for the last event + float mLoopsFractionPairs = 0.08; // Fraction of loopers from Pairs + int mInteractionRate = 50000; // Interaction rate in Hz +}; +#endif // GENERATORS_WITH_TPCLOOPERS + +} // namespace eventgen +} // namespace o2 + +#endif // ALICEO2_EVENTGEN_TPCLOOPERS_H_ \ No newline at end of file diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h new file mode 100644 index 0000000000000..87e4510d6e617 --- /dev/null +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -0,0 +1,54 @@ +// Copyright 2024-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. + +/// \author M+Giacalone - September 2025 + +#ifndef ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ +#define ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace eventgen +{ + +/** + ** a parameter class/struct to keep the settings of + ** the TPC loopers event-generator and + ** allow the user to modify them + **/ +struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper { + bool loopersVeto = false; // if true, no loopers are generated + // Current files are set to custom user CCDB paths, TO BE CHANGED + std::string model_pairs = "ccdb://Users/m/mgiacalo/WGAN_ExtGenPair"; // ONNX model for e+e- pair production + std::string model_compton = "ccdb://Users/m/mgiacalo/WGAN_ExtGenCompton"; // ONNX model for Compton scattering + std::string poisson = "${O2_ROOT}/share/Generators/TPCLoopers/poisson_params.csv"; // file with Poissonian parameters + std::string gauss = "${O2_ROOT}/share/Generators/TPCLoopers/gaussian_params.csv"; // file with Gaussian parameters + std::string scaler_pair = "${O2_ROOT}/share/Generators/TPCLoopers/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production + std::string scaler_compton = "${O2_ROOT}/share/Generators/TPCLoopers/ScalerComptonParams.json"; // file with scaler parameters for Compton scattering + std::string nclxrate = "ccdb://Users/m/mgiacalo/ClustersTrackRatio"; // file with clusters/rate information per orbit + std::string colsys = "PbPb"; // collision system (PbPb or pp) + int intrate = -1; // Automatic IR from collision context if -1, else user-defined interaction rate in Hz + bool flat_gas = true; // if true, the gas density is considered flat in the TPC volume + unsigned int nFlatGasLoopers = 500; // number of loopers to be generated per event in case of flat gas [currently unused, kept for possible future debug developments] + float fraction_pairs = 0.08; // fraction of loopers [currently unused, kept for possible future debug developments] + float multiplier[2] = {1., 1.}; // multiplier for pairs and compton loopers for Poissonian and Gaussian sampling + unsigned int fixedNLoopers[2] = {1, 1}; // fixed number of loopers coming from pairs and compton electrons - valid if flat gas is false and both Poisson and Gaussian params files are empty + float adjust_flatgas = 0.f; // adjustment for the number of flat gas loopers per orbit (in percentage, e.g. -0.1 = -10%) [-1, inf)] + O2ParamDef(GenTPCLoopersParam, "GenTPCLoopers"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ diff --git a/Generators/share/TPCLoopers/README.md b/Generators/share/TPCLoopers/README.md new file mode 100644 index 0000000000000..0e0ac858b8809 --- /dev/null +++ b/Generators/share/TPCLoopers/README.md @@ -0,0 +1,79 @@ +# TPC Loopers Generator - Parameter Files + +This directory contains parameter files used by the TPC Loopers event generator in ALICE O2. + +## Overview + +The TPC Loopers generator uses pre-trained ONNX models to generate realistic looper particles based on machine learning models trained on full GEANT4 slow neutron transport simulations. The parameter files in this directory provide: +- Example statistical distribution parameters for sampling the number of loopers per event +- **Mandatory** scaling parameters for transforming the ONNX model outputs to physical values + +## Files Description + +### Statistical Sampling Parameters + +The files provided in the folder are examples based on the training dataset. + +#### `gaussian_params.csv` +Parameters for Gaussian distribution used to sample the number of Compton electrons per event. + +**Format:** Four values (one per line) +1. Mean (μ) +2. Standard deviation (σ) +3. Minimum value +4. Maximum value + +#### `poisson_params.csv` +Parameters for Poisson distribution used to sample the number of electron-positron pairs per event. + +**Format:** Three values (one per line) +1. Lambda (λ) parameter +2. Minimum value +3. Maximum value + +### Scaler Parameters + +These JSON files contain the parameters for inverse transformation of the ONNX models output. They should be kept as they are +unless a new version of the models is released. + +#### `ScalerComptonParams.json` +Scaler parameters for Compton electron generation model. + +**Structure:** +```json +{ + "normal": { + "min": [array of 5 min values for min-max normalization], + "max": [array of 5 max values for min-max normalization] + }, + "outlier": { + "center": [array of 2 center values for robust scaling], + "scale": [array of 2 scale values for robust scaling] + } +} +``` + +- **normal**: Min-max normalization parameters for standard features (`Px`, `Py`, `Pz`, `VertexCoordinatesX`, `VertexCoordinatesY`) +- **outlier**: Robust scaler parameters (center and scale) for outlier features (`VertexCoordinatesZ`,`time`) + +#### `ScalerPairParams.json` +Scaler parameters for electron-positron pair generation model. + +**Structure:** +```json +{ + "normal": { + "min": [array of 8 min values for min-max normalization], + "max": [array of 8 max values for min-max normalization] + }, + "outlier": { + "center": [array of 2 center values for robust scaling], + "scale": [array of 2 scale values for robust scaling] + } +} +``` + +- **normal**: Min-max normalization parameters for standard features (`Px_e`, `Py_e`, `Pz_e`,`Px_p`, `Py_p`, `Pz_p`, `VertexCoordinatesX`, `VertexCoordinatesY`) +- **outlier**: Robust scaler parameters (center and scale) for outlier features (`VertexCoordinatesZ`,`time`) +--- +*Author: M. Giacalone - September 2025* diff --git a/Generators/share/TPCLoopers/ScalerComptonParams.json b/Generators/share/TPCLoopers/ScalerComptonParams.json new file mode 100644 index 0000000000000..157647fee2db7 --- /dev/null +++ b/Generators/share/TPCLoopers/ScalerComptonParams.json @@ -0,0 +1,28 @@ +{ + "normal": { + "min": [ + -0.0108811147511005, + -0.0098758740350604, + -0.0103233363479375, + -260.0542297363281, + -259.80059814453125 + ], + "max": [ + 0.0108060473576188, + 0.0103057539090514, + 0.0106524610891938, + 260.0343933105469, + 259.62890625 + ] + }, + "outlier": { + "center": [ + -71.39387130737305, + 96791.23828125 + ], + "scale": [ + 265.9389114379883, + 230762.30981445312 + ] + } +} \ No newline at end of file diff --git a/Generators/share/TPCLoopers/ScalerPairParams.json b/Generators/share/TPCLoopers/ScalerPairParams.json new file mode 100644 index 0000000000000..57cdac421d3f6 --- /dev/null +++ b/Generators/share/TPCLoopers/ScalerPairParams.json @@ -0,0 +1,34 @@ +{ + "normal": { + "min": [ + -0.0073022879660129, + -0.0077305701561272, + -0.0076750442385673, + -0.0082916170358657, + -0.0079681202769279, + -0.0077468422241508, + -255.6164093017578, + -252.9441680908203 + ], + "max": [ + 0.007688719779253, + 0.0077241472899913, + 0.0075828479602932, + 0.00813714787364, + 0.0083825681358575, + 0.0073839174583554, + 256.2904968261719, + 253.4925842285156 + ] + }, + "outlier": { + "center": [ + -79.66580963134766, + 141535.640625 + ], + "scale": [ + 250.8921127319336, + 222363.16015625 + ] + } +} \ No newline at end of file diff --git a/Generators/share/TPCLoopers/gaussian_params.csv b/Generators/share/TPCLoopers/gaussian_params.csv new file mode 100644 index 0000000000000..8e07c22dd30bf --- /dev/null +++ b/Generators/share/TPCLoopers/gaussian_params.csv @@ -0,0 +1,4 @@ +9.611554230339172022e+01 +1.963570744941765867e+01 +4.300000000000000000e+01 +1.690000000000000000e+02 diff --git a/Generators/share/TPCLoopers/poisson_params.csv b/Generators/share/TPCLoopers/poisson_params.csv new file mode 100644 index 0000000000000..ef26bd973d34c --- /dev/null +++ b/Generators/share/TPCLoopers/poisson_params.csv @@ -0,0 +1,3 @@ +3.165383056343737511e+00 +1.000000000000000000e+00 +1.200000000000000000e+01 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 9204ede98215e..ecea311c94de7 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -17,11 +17,20 @@ #include "SimulationDataFormat/MCEventHeader.h" #include "SimulationDataFormat/ParticleStatus.h" #include "SimulationDataFormat/MCGenProperties.h" +#include #include "FairPrimaryGenerator.h" #include #include #include "TClonesArray.h" #include "TParticle.h" +#include "TSystem.h" +#include "TGrid.h" +#include "CCDB/BasicCCDBManager.h" +#include +#ifdef GENERATORS_WITH_TPCLOOPERS +#include "Generators/TPCLoopers.h" +#include "Generators/TPCLoopersParam.h" +#endif namespace o2 { @@ -39,6 +48,25 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), /** default constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; +#ifdef GENERATORS_WITH_TPCLOOPERS + const auto& simConfig = o2::conf::SimConfig::Instance(); + const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); + if (!loopersParam.loopersVeto) { + bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); + if (transport) { + bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); + if (tpcActive) { + if (initTPCLoopersGen()) { + mAddTPCLoopers = kTRUE; + } + } else { + LOG(info) << "TPC not active in readout detectors: loopers fast generator disabled."; + } + } + } else { + LOG(info) << "Loopers fast generator turned OFF with veto flag."; + } +#endif } /*****************************************************************/ @@ -49,10 +77,144 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /** constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; +#ifdef GENERATORS_WITH_TPCLOOPERS + const auto& simConfig = o2::conf::SimConfig::Instance(); + const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); + if (!loopersParam.loopersVeto) { + bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); + if (transport) { + bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); + if (tpcActive) { + if (initTPCLoopersGen()) { + mAddTPCLoopers = kTRUE; + } + } else { + LOG(info) << "TPC not active in readout detectors: loopers fast generator disabled."; + } + } + } else { + LOG(info) << "Loopers fast generator turned OFF with veto flag."; + } +#endif } /*****************************************************************/ +Generator::~Generator() +{ + /** destructor **/ +#ifdef GENERATORS_WITH_TPCLOOPERS + if (mTPCLoopersGen) { + delete mTPCLoopersGen; + mTPCLoopersGen = nullptr; + } +#endif +} + +/*****************************************************************/ +#ifdef GENERATORS_WITH_TPCLOOPERS +bool Generator::initTPCLoopersGen() +{ + // Expand all environment paths + const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); + std::string model_pairs = gSystem->ExpandPathName(loopersParam.model_pairs.c_str()); + std::string model_compton = gSystem->ExpandPathName(loopersParam.model_compton.c_str()); + std::string nclxrate = gSystem->ExpandPathName(loopersParam.nclxrate.c_str()); + const auto& scaler_pair = gSystem->ExpandPathName(loopersParam.scaler_pair.c_str()); + const auto& scaler_compton = gSystem->ExpandPathName(loopersParam.scaler_compton.c_str()); + const auto& poisson = gSystem->ExpandPathName(loopersParam.poisson.c_str()); + const auto& gauss = gSystem->ExpandPathName(loopersParam.gauss.c_str()); + const auto& flat_gas = loopersParam.flat_gas; + const auto& colsys = loopersParam.colsys; + if (flat_gas) { + if (colsys != "PbPb" && colsys != "pp") { + LOG(warning) << "Automatic background loopers configuration supports only 'pp' and 'PbPb' systems."; + LOG(warning) << "Fast loopers generator will remain OFF."; + return kFALSE; + } + bool isContext = std::filesystem::exists("collisioncontext.root"); + if (!isContext) { + LOG(warning) << "Warning: No collisioncontext.root file found!"; + LOG(warning) << "Loopers will be kept OFF."; + return kFALSE; + } + } + std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; + unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; + unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; + const std::array models = {model_pairs, model_compton, nclxrate}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx", "nclxrate.root"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://"), models[2].starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://"), models[2].starts_with("ccdb://")}; + if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) { return v; })) { + if (!gGrid) { + TGrid::Connect("alien://"); + if (!gGrid) { + LOG(fatal) << "AliEn connection failed, check token."; + exit(1); + } + } + for (size_t i = 0; i < models.size(); ++i) { + if (isAlien[i] && !TFile::Cp(models[i].c_str(), local_names[i].c_str())) { + LOG(fatal) << "Error: Model file " << models[i] << " does not exist!"; + exit(1); + } + } + } + if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) { return v; })) { + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + ccdb.setURL("http://alice-ccdb.cern.ch"); + // Get underlying CCDB API from BasicCCDBManager + auto& ccdb_api = ccdb.getCCDBAccessor(); + for (size_t i = 0; i < models.size(); ++i) { + if (isCCDB[i]) { + auto model_path = models[i].substr(7); // Remove "ccdb://" + // Treat filename if provided in the CCDB path + auto extension = model_path.find(".onnx"); + if (extension != std::string::npos) { + auto last_slash = model_path.find_last_of('/'); + model_path = model_path.substr(0, last_slash); + } + std::map filter; + if (!ccdb_api.retrieveBlob(model_path, "./", filter, o2::ccdb::getCurrentTimestamp(), false, local_names[i].c_str())) { + LOG(fatal) << "Error: issues in retrieving " << model_path << " from CCDB!"; + exit(1); + } + } + } + } + model_pairs = isAlien[0] || isCCDB[0] ? local_names[0] : model_pairs; + model_compton = isAlien[1] || isCCDB[1] ? local_names[1] : model_compton; + nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; + try { + // Create the TPC loopers generator with the provided parameters + mTPCLoopersGen = new o2::eventgen::GenTPCLoopers(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + const auto& intrate = loopersParam.intrate; + // Configure the generator with flat gas loopers defined per orbit with clusters/track info + // If intrate is negative (default), automatic IR from collisioncontext.root will be used + if (flat_gas) { + mTPCLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); + mTPCLoopersGen->SetAdjust(loopersParam.adjust_flatgas); + } else { + // Otherwise, Poisson+Gauss sampling or fixed number of loopers per event will be used + // Multiplier is applied only with distribution sampling + // This configuration can be used for testing purposes, in all other cases flat gas is recommended + mTPCLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); + mTPCLoopersGen->SetMultiplier(multiplier); + } + LOG(info) << "TPC Loopers generator initialized successfully"; + } catch (const std::exception& e) { + LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); + delete mTPCLoopersGen; + mTPCLoopersGen = nullptr; + return kFALSE; + } + return kTRUE; +} +#endif + +/*****************************************************************/ + Bool_t Generator::Init() { @@ -64,6 +226,41 @@ Bool_t /*****************************************************************/ +Bool_t + Generator::finalizeEvent() +{ +#ifdef GENERATORS_WITH_TPCLOOPERS + if (mAddTPCLoopers) { + if (!mTPCLoopersGen) { + LOG(error) << "Loopers generator not initialized"; + return kFALSE; + } + + // Generate loopers using the initialized TPC loopers generator + if (!mTPCLoopersGen->generateEvent()) { + LOG(error) << "Failed to generate loopers event"; + return kFALSE; + } + if (mTPCLoopersGen->getNLoopers() == 0) { + LOG(warning) << "No loopers generated for this event"; + return kTRUE; + } + const auto& looperParticles = mTPCLoopersGen->importParticles(); + if (looperParticles.empty()) { + LOG(error) << "Failed to import loopers particles"; + return kFALSE; + } + // Append the generated looper particles to the main particle list + mParticles.insert(mParticles.end(), looperParticles.begin(), looperParticles.end()); + + LOG(debug) << "Added " << looperParticles.size() << " looper particles"; + } +#endif + return kTRUE; +} + +/*****************************************************************/ + Bool_t Generator::ReadEvent(FairPrimaryGenerator* primGen) { @@ -91,6 +288,12 @@ Bool_t return kFALSE; } + /** Event finalization**/ + if (!finalizeEvent()) { + LOG(error) << "ReadEvent failed in finalizeEvent"; + return kFALSE; + } + if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; } diff --git a/Generators/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index 8a6001b2cd5e6..1cc2659460a4b 100644 --- a/Generators/src/GeneratorFactory.cxx +++ b/Generators/src/GeneratorFactory.cxx @@ -93,6 +93,12 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair o2::O2DatabasePDG::addALICEParticles(TDatabasePDG::Instance()); auto genconfig = conf.getGenerator(); +#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3) + if (GeneratorHybridParam::Instance().switchExtToHybrid && (genconfig.compare("external") == 0 || genconfig.compare("extgen") == 0)) { + LOG(info) << "Switching external generator to hybrid mode"; + genconfig = "hybrid"; + } +#endif LOG(info) << "** Generator to use: '" << genconfig << "'"; if (genconfig.compare("boxgen") == 0) { // a simple "box" generator configurable via BoxGunparam @@ -279,13 +285,8 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair LOG(fatal) << "No configuration file provided for hybrid generator"; return; } - // check if file named config exists and it's not empty - else if (gSystem->AccessPathName(config.c_str())) { - LOG(fatal) << "Configuration file for hybrid generator does not exist"; - return; - } - auto hybrid = new o2::eventgen::GeneratorHybrid(config); - primGen->AddGenerator(hybrid); + auto& hybrid = o2::eventgen::GeneratorHybrid::Instance(config); + primGen->AddGenerator(&hybrid); #endif } else { LOG(fatal) << "Invalid generator"; diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index 83a694703c259..f853b772e3cd3 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -23,6 +23,12 @@ namespace o2 namespace eventgen { +GeneratorHybrid& GeneratorHybrid::Instance(const std::string& inputgens) +{ + static GeneratorHybrid instance(inputgens); + return instance; +} + GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) { // This generator has trivial unit conversions @@ -414,6 +420,7 @@ bool GeneratorHybrid::importParticles() mMCEventHeader.clearInfo(); if (mCocktailMode) { // in cocktail mode we need to merge the particles from the different generators + bool baseGen = true; // first generator of the cocktail is used as reference to update the event header information for (auto subIndex : subGenIndex) { LOG(info) << "Importing particles for task " << subIndex; auto subParticles = gens[subIndex]->getParticles(); @@ -435,8 +442,10 @@ bool GeneratorHybrid::importParticles() } mParticles.insert(mParticles.end(), subParticles.begin(), subParticles.end()); - // fetch the event Header information from the underlying generator - gens[subIndex]->updateHeader(&mMCEventHeader); + if (baseGen) { + gens[subIndex]->updateHeader(&mMCEventHeader); + baseGen = false; + } mInputTaskQueue.push(subIndex); mTasksStarted++; } @@ -475,7 +484,9 @@ bool GeneratorHybrid::importParticles() void GeneratorHybrid::updateHeader(o2::dataformats::MCEventHeader* eventHeader) { if (eventHeader) { - // we forward the original header information if any + // Forward the base class fields from FairMCEventHeader + static_cast(*eventHeader) = static_cast(mMCEventHeader); + // Copy the key-value store info eventHeader->copyInfoFrom(mMCEventHeader); // put additional information about @@ -609,17 +620,23 @@ Bool_t GeneratorHybrid::confSetter(const auto& gen) Bool_t GeneratorHybrid::parseJSON(const std::string& path) { + auto expandedPath = o2::utils::expandShellVarsInFileName(path); + // Check if configuration file exists + if (gSystem->AccessPathName(expandedPath.c_str())) { + LOG(fatal) << "Configuration file " << expandedPath << " for hybrid generator does not exist"; + return false; + } // Parse JSON file to build map - std::ifstream fileStream(path, std::ios::in); + std::ifstream fileStream(expandedPath, std::ios::in); if (!fileStream.is_open()) { - LOG(error) << "Cannot open " << path; + LOG(error) << "Cannot open " << expandedPath; return false; } rapidjson::IStreamWrapper isw(fileStream); rapidjson::Document doc; doc.ParseStream(isw); if (doc.HasParseError()) { - LOG(error) << "Error parsing provided json file " << path; + LOG(error) << "Error parsing provided json file " << expandedPath; LOG(error) << " - Error -> " << rapidjson::GetParseError_En(doc.GetParseError()); return false; } diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 2b8d42f86bf9b..24b3f2e452498 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -35,6 +35,10 @@ #pragma link C++ class o2::eventgen::GeneratorFromEventPool + ; #pragma link C++ class o2::eventgen::GeneratorEventPoolParam + ; #pragma link C++ class o2::eventgen::EventPoolGenConfig + ; +#ifdef GENERATORS_WITH_TPCLOOPERS +#pragma link C++ class o2::eventgen::GenTPCLoopers + ; +#pragma link C++ class o2::eventgen::GenTPCLoopersParam + ; +#endif #pragma link C++ class o2::conf::ConfigurableParamPromoter < o2::eventgen::GeneratorEventPoolParam, o2::eventgen::EventPoolGenConfig> + ; #ifdef GENERATORS_WITH_HEPMC3 #pragma link C++ class o2::eventgen::GeneratorHepMC + ; diff --git a/Generators/src/PrimaryGenerator.cxx b/Generators/src/PrimaryGenerator.cxx index ee7c6a16330f9..e7b70108a5d4b 100644 --- a/Generators/src/PrimaryGenerator.cxx +++ b/Generators/src/PrimaryGenerator.cxx @@ -81,7 +81,7 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) /** generate event **/ /** normal generation if no embedding **/ - if (!mEmbedTree) { + if (!mEmbedTree || mEmbedIndex < 0) { fixInteractionVertex(); // <-- always fixes vertex outside of FairROOT auto ret = FairPrimaryGenerator::GenerateEvent(pStack); if (ret) { @@ -91,17 +91,18 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) } /** this is for embedding **/ - - /** setup interaction vertex **/ - mEmbedTree->GetEntry(mEmbedIndex); - setInteractionVertex(mEmbedEvent); - - /** notify event generators **/ - auto genList = GetListOfGenerators(); - for (int igen = 0; igen < genList->GetEntries(); ++igen) { - auto o2gen = dynamic_cast(genList->At(igen)); - if (o2gen) { - o2gen->notifyEmbedding(mEmbedEvent); + if (mEmbedIndex >= 0) { + /** setup interaction vertex **/ + mEmbedTree->GetEntry(mEmbedIndex); + setInteractionVertex(mEmbedEvent); + + /** notify event generators **/ + auto genList = GetListOfGenerators(); + for (int igen = 0; igen < genList->GetEntries(); ++igen) { + auto o2gen = dynamic_cast(genList->At(igen)); + if (o2gen) { + o2gen->notifyEmbedding(mEmbedEvent); + } } } diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx new file mode 100644 index 0000000000000..6e5af7c0c84d8 --- /dev/null +++ b/Generators/src/TPCLoopers.cxx @@ -0,0 +1,486 @@ +// Copyright 2024-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. + +/// \author M+Giacalone - September 2025 + +#include "Generators/TPCLoopers.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsRaw/HBFUtils.h" +#include "TF1.h" +#include +#include +#include "SimulationDataFormat/MCGenProperties.h" +#include +#include +#include "TDatabasePDG.h" + +// Static Ort::Env instance for multiple onnx model loading +Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv"); + +// This class is responsible for loading the scaler parameters from a JSON file +// and applying the inverse transformation to the generated data. + +void Scaler::load(const std::string& filename) +{ + std::ifstream file(filename); + if (!file.is_open()) { + throw std::runtime_error("Error: Could not open scaler file!"); + } + + std::string json_str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + rapidjson::Document doc; + doc.Parse(json_str.c_str()); + + if (doc.HasParseError()) { + throw std::runtime_error("Error: JSON parsing failed!"); + } + + normal_min = jsonArrayToVector(doc["normal"]["min"]); + normal_max = jsonArrayToVector(doc["normal"]["max"]); + outlier_center = jsonArrayToVector(doc["outlier"]["center"]); + outlier_scale = jsonArrayToVector(doc["outlier"]["scale"]); +} + +std::vector Scaler::inverse_transform(const std::vector& input) +{ + std::vector output; + for (int i = 0; i < input.size(); ++i) { + if (i < input.size() - 2) { + output.push_back(input[i] * (normal_max[i] - normal_min[i]) + normal_min[i]); + } else { + output.push_back(input[i] * outlier_scale[i - (input.size() - 2)] + outlier_center[i - (input.size() - 2)]); + } + } + + return output; +} + +std::vector Scaler::jsonArrayToVector(const rapidjson::Value& jsonArray) +{ + std::vector vec; + for (int i = 0; i < jsonArray.Size(); ++i) { + vec.push_back(jsonArray[i].GetDouble()); + } + return vec; +} + +// This class loads the ONNX model and generates samples using it. + +ONNXGenerator::ONNXGenerator(Ort::Env& shared_env, const std::string& model_path) + : env(shared_env), session(env, model_path.c_str(), Ort::SessionOptions{}) +{ + // Create session options + Ort::SessionOptions session_options; + session = Ort::Session(env, model_path.c_str(), session_options); +} + +std::vector ONNXGenerator::generate_sample() +{ + Ort::AllocatorWithDefaultOptions allocator; + + // Generate a latent vector (z) + std::vector z(100); + for (auto& v : z) { + v = rand_gen.Gaus(0.0, 1.0); + } + + // Prepare input tensor + std::vector input_shape = {1, 100}; + // Get memory information + Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + + // Create input tensor correctly + Ort::Value input_tensor = Ort::Value::CreateTensor( + memory_info, z.data(), z.size(), input_shape.data(), input_shape.size()); + // Run inference + const char* input_names[] = {"z"}; + const char* output_names[] = {"output"}; + auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, 1); + + // Extract output + float* output_data = output_tensors.front().GetTensorMutableData(); + // Get the size of the output tensor + auto output_tensor_info = output_tensors.front().GetTensorTypeAndShapeInfo(); + size_t output_data_size = output_tensor_info.GetElementCount(); // Total number of elements in the tensor + std::vector output; + for (int i = 0; i < output_data_size; ++i) { + output.push_back(output_data[i]); + } + + return output; +} + +namespace o2 +{ +namespace eventgen +{ + +GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton, + std::string poisson, std::string gauss, std::string scaler_pair, + std::string scaler_compton) +{ + // Checking if the model files exist and are not empty + std::ifstream model_file[2]; + model_file[0].open(model_pairs); + model_file[1].open(model_compton); + if (!model_file[0].is_open() || model_file[0].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Pairs model file is empty or does not exist!"; + exit(1); + } + if (!model_file[1].is_open() || model_file[1].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Compton model file is empty or does not exist!"; + exit(1); + } + model_file[0].close(); + model_file[1].close(); + // Checking if the scaler files exist and are not empty + std::ifstream scaler_file[2]; + scaler_file[0].open(scaler_pair); + scaler_file[1].open(scaler_compton); + if (!scaler_file[0].is_open() || scaler_file[0].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Pairs scaler file is empty or does not exist!"; + exit(1); + } + if (!scaler_file[1].is_open() || scaler_file[1].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Compton scaler file is empty or does not exist!"; + exit(1); + } + scaler_file[0].close(); + scaler_file[1].close(); + // Checking if the poisson file exists and it's not empty + if (poisson != "" && poisson != "None" && poisson != "none") { + std::ifstream poisson_file(poisson); + if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Poisson file is empty or does not exist!"; + exit(1); + } else { + poisson_file >> mPoisson[0] >> mPoisson[1] >> mPoisson[2]; + poisson_file.close(); + mPoissonSet = true; + } + } + // Checking if the gauss file exists and it's not empty + if (gauss != "" && gauss != "None" && gauss != "none") { + std::ifstream gauss_file(gauss); + if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Gauss file is empty or does not exist!"; + exit(1); + } else { + gauss_file >> mGauss[0] >> mGauss[1] >> mGauss[2] >> mGauss[3]; + gauss_file.close(); + mGaussSet = true; + } + } + mONNX_pair = std::make_unique(global_env, model_pairs); + mScaler_pair = std::make_unique(); + mScaler_pair->load(scaler_pair); + mONNX_compton = std::make_unique(global_env, model_compton); + mScaler_compton = std::make_unique(); + mScaler_compton->load(scaler_compton); +} + +Bool_t GenTPCLoopers::generateEvent() +{ + // Clear the vector of pairs + mGenPairs.clear(); + // Clear the vector of compton electrons + mGenElectrons.clear(); + if (mFlatGas) { + unsigned int nLoopers, nLoopersPairs, nLoopersCompton; + LOG(debug) << "mCurrentEvent is " << mCurrentEvent; + LOG(debug) << "Current event time: " << ((mCurrentEvent < mInteractionTimeRecords.size() - 1) ? std::to_string(mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns()) : std::to_string(mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns())) << " ns"; + LOG(debug) << "Current time offset wrt BC: " << mInteractionTimeRecords[mCurrentEvent].getTimeOffsetWrtBC() << " ns"; + mTimeLimit = (mCurrentEvent < mInteractionTimeRecords.size() - 1) ? mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns() : mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns(); + // With flat gas the number of loopers are adapted based on time interval widths + // The denominator is either the LHC orbit (if mFlatGasOrbit is true) or the mean interaction time record interval + nLoopers = mFlatGasOrbit ? (mFlatGasNumber * (mTimeLimit / o2::constants::lhc::LHCOrbitNS)) : (mFlatGasNumber * (mTimeLimit / mIntTimeRecMean)); + nLoopersPairs = static_cast(std::round(nLoopers * mLoopsFractionPairs)); + nLoopersCompton = nLoopers - nLoopersPairs; + SetNLoopers(nLoopersPairs, nLoopersCompton); + LOG(info) << "Flat gas loopers: " << nLoopers << " (pairs: " << nLoopersPairs << ", compton: " << nLoopersCompton << ")"; + generateEvent(mTimeLimit); + mCurrentEvent++; + } else { + // Set number of loopers if poissonian params are available + if (mPoissonSet) { + mNLoopersPairs = static_cast(std::round(mMultiplier[0] * PoissonPairs())); + LOG(debug) << "Generated loopers pairs (Poisson): " << mNLoopersPairs; + } + if (mGaussSet) { + mNLoopersCompton = static_cast(std::round(mMultiplier[1] * GaussianElectrons())); + LOG(debug) << "Generated compton electrons (Gauss): " << mNLoopersCompton; + } + // Generate pairs + for (int i = 0; i < mNLoopersPairs; ++i) { + std::vector pair = mONNX_pair->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_pair = mScaler_pair->inverse_transform(pair); + mGenPairs.push_back(transformed_pair); + } + // Generate compton electrons + for (int i = 0; i < mNLoopersCompton; ++i) { + std::vector electron = mONNX_compton->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_electron = mScaler_compton->inverse_transform(electron); + mGenElectrons.push_back(transformed_electron); + } + } + return true; +} + +Bool_t GenTPCLoopers::generateEvent(double time_limit) +{ + LOG(info) << "Time constraint for loopers: " << time_limit << " ns"; + // Generate pairs + for (int i = 0; i < mNLoopersPairs; ++i) { + std::vector pair = mONNX_pair->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_pair = mScaler_pair->inverse_transform(pair); + transformed_pair[9] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds + mGenPairs.push_back(transformed_pair); + } + // Generate compton electrons + for (int i = 0; i < mNLoopersCompton; ++i) { + std::vector electron = mONNX_compton->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_electron = mScaler_compton->inverse_transform(electron); + transformed_electron[6] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds + mGenElectrons.push_back(transformed_electron); + } + LOG(info) << "Generated Particles with time limit"; + return true; +} + +std::vector GenTPCLoopers::importParticles() +{ + std::vector particles; + const double mass_e = TDatabasePDG::Instance()->GetParticle(11)->Mass(); + const double mass_p = TDatabasePDG::Instance()->GetParticle(-11)->Mass(); + // Get looper pairs from the event + for (auto& pair : mGenPairs) { + double px_e, py_e, pz_e, px_p, py_p, pz_p; + double vx, vy, vz, time; + double e_etot, p_etot; + px_e = pair[0]; + py_e = pair[1]; + pz_e = pair[2]; + px_p = pair[3]; + py_p = pair[4]; + pz_p = pair[5]; + vx = pair[6]; + vy = pair[7]; + vz = pair[8]; + time = pair[9]; + e_etot = TMath::Sqrt(px_e * px_e + py_e * py_e + pz_e * pz_e + mass_e * mass_e); + p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mass_p * mass_p); + // Push the electron + TParticle electron(11, 1, -1, -1, -1, -1, px_e, py_e, pz_e, e_etot, vx, vy, vz, time / 1e9); + electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(electron.GetStatusCode(), 0).fullEncoding); + electron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(electron.GetStatusCode()) == 1); + particles.push_back(electron); + // Push the positron + TParticle positron(-11, 1, -1, -1, -1, -1, px_p, py_p, pz_p, p_etot, vx, vy, vz, time / 1e9); + positron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(positron.GetStatusCode(), 0).fullEncoding); + positron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(positron.GetStatusCode()) == 1); + particles.push_back(positron); + } + // Get compton electrons from the event + for (auto& compton : mGenElectrons) { + double px, py, pz; + double vx, vy, vz, time; + double etot; + px = compton[0]; + py = compton[1]; + pz = compton[2]; + vx = compton[3]; + vy = compton[4]; + vz = compton[5]; + time = compton[6]; + etot = TMath::Sqrt(px * px + py * py + pz * pz + mass_e * mass_e); + // Push the electron + TParticle electron(11, 1, -1, -1, -1, -1, px, py, pz, etot, vx, vy, vz, time / 1e9); + electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(electron.GetStatusCode(), 0).fullEncoding); + electron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(electron.GetStatusCode()) == 1); + particles.push_back(electron); + } + + return particles; +} + +unsigned int GenTPCLoopers::PoissonPairs() +{ + unsigned int poissonValue; + do { + // Generate a Poisson-distributed random number with mean mPoisson[0] + poissonValue = mRandGen.Poisson(mPoisson[0]); + } while (poissonValue < mPoisson[1] || poissonValue > mPoisson[2]); // Regenerate if out of range + + return poissonValue; +} + +unsigned int GenTPCLoopers::GaussianElectrons() +{ + unsigned int gaussValue; + do { + // Generate a Normal-distributed random number with mean mGass[0] and stddev mGauss[1] + gaussValue = mRandGen.Gaus(mGauss[0], mGauss[1]); + } while (gaussValue < mGauss[2] || gaussValue > mGauss[3]); // Regenerate if out of range + + return gaussValue; +} + +void GenTPCLoopers::SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton) +{ + if (mFlatGas) { + mNLoopersPairs = nsig_pair; + mNLoopersCompton = nsig_compton; + } else { + if (mPoissonSet) { + LOG(info) << "Poissonian parameters correctly loaded."; + } else { + mNLoopersPairs = nsig_pair; + } + if (mGaussSet) { + LOG(info) << "Gaussian parameters correctly loaded."; + } else { + mNLoopersCompton = nsig_compton; + } + } +} + +void GenTPCLoopers::SetMultiplier(const std::array& mult) +{ + // Multipliers will work only if the poissonian and gaussian parameters are set + // otherwise they will be ignored + if (mult[0] < 0 || mult[1] < 0) { + LOG(fatal) << "Error: Multiplier values must be non-negative!"; + exit(1); + } else { + LOG(info) << "Multiplier values set to: Pair = " << mult[0] << ", Compton = " << mult[1]; + mMultiplier[0] = mult[0]; + mMultiplier[1] = mult[1]; + } +} + +void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit) +{ + mFlatGas = flat; + if (mFlatGas) { + if (nloopers_orbit > 0) { + mFlatGasOrbit = true; + mFlatGasNumber = nloopers_orbit; + LOG(info) << "Flat gas loopers will be generated using orbit reference."; + } else { + mFlatGasOrbit = false; + if (number < 0) { + LOG(warn) << "Warning: Number of loopers per event must be non-negative! Switching option off."; + mFlatGas = false; + mFlatGasNumber = -1; + } else { + mFlatGasNumber = number; + } + } + if (mFlatGas) { + mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr; + mCollisionContext = mContextFile ? (o2::steer::DigitizationContext*)mContextFile->Get("DigitizationContext") : nullptr; + mInteractionTimeRecords = mCollisionContext ? mCollisionContext->getEventRecords() : std::vector{}; + if (mInteractionTimeRecords.empty()) { + LOG(error) << "Error: No interaction time records found in the collision context!"; + exit(1); + } else { + LOG(info) << "Interaction Time records has " << mInteractionTimeRecords.size() << " entries."; + mCollisionContext->printCollisionSummary(); + } + for (int c = 0; c < mInteractionTimeRecords.size() - 1; c++) { + mIntTimeRecMean += mInteractionTimeRecords[c + 1].bc2ns() - mInteractionTimeRecords[c].bc2ns(); + } + mIntTimeRecMean /= (mInteractionTimeRecords.size() - 1); // Average interaction time record used as reference + const auto& hbfUtils = o2::raw::HBFUtils::Instance(); + // Get the start time of the second orbit after the last interaction record + const auto& lastIR = mInteractionTimeRecords.back(); + o2::InteractionRecord finalOrbitIR(0, lastIR.orbit + 2); // Final orbit, BC = 0 + mTimeEnd = finalOrbitIR.bc2ns(); + LOG(debug) << "Final orbit start time: " << mTimeEnd << " ns while last interaction record time is " << mInteractionTimeRecords.back().bc2ns() << " ns"; + } + } else { + mFlatGasNumber = -1; + } + LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; +} + +void GenTPCLoopers::setFractionPairs(float fractionPairs) +{ + if (fractionPairs < 0 || fractionPairs > 1) { + LOG(fatal) << "Error: Loops fraction for pairs must be in the range [0, 1]."; + exit(1); + } + mLoopsFractionPairs = fractionPairs; + LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; +} + +void GenTPCLoopers::SetRate(const std::string& rateFile, bool isPbPb = true, int intRate) +{ + // Checking if the rate file exists and is not empty + TFile rate_file(rateFile.c_str(), "READ"); + if (!rate_file.IsOpen() || rate_file.IsZombie()) { + LOG(fatal) << "Error: Rate file is empty or does not exist!"; + exit(1); + } + const char* fitName = isPbPb ? "fitPbPb" : "fitpp"; + auto fit = (TF1*)rate_file.Get(fitName); + if (!fit) { + LOG(fatal) << "Error: Could not find fit function '" << fitName << "' in rate file!"; + exit(1); + } + mInteractionRate = intRate; + if (mInteractionRate < 0) { + mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr; + if (!mContextFile || mContextFile->IsZombie()) { + LOG(fatal) << "Error: Interaction rate not provided and collision context file not found!"; + exit(1); + } + mCollisionContext = (o2::steer::DigitizationContext*)mContextFile->Get("DigitizationContext"); + mInteractionRate = std::floor(mCollisionContext->getDigitizerInteractionRate()); + LOG(info) << "Interaction rate retrieved from collision context: " << mInteractionRate << " Hz"; + if (mInteractionRate < 0) { + LOG(fatal) << "Error: Invalid interaction rate retrieved from collision context!"; + exit(1); + } + } + auto ref = static_cast(std::floor(fit->Eval(mInteractionRate / 1000.))); // fit expects rate in kHz + rate_file.Close(); + if (ref <= 0) { + LOG(fatal) << "Computed flat gas number reference per orbit is <=0"; + exit(1); + } else { + LOG(info) << "Set flat gas number to " << ref << " loopers per orbit using " << fitName << " from " << mInteractionRate << " Hz interaction rate."; + auto flat = true; + setFlatGas(flat, -1, ref); + } +} + +void GenTPCLoopers::SetAdjust(float adjust) +{ + if (mFlatGas && mFlatGasOrbit && adjust >= -1.f && adjust != 0.f) { + LOG(info) << "Adjusting flat gas number per orbit by " << adjust * 100.f << "%"; + mFlatGasNumber = static_cast(std::round(mFlatGasNumber * (1.f + adjust))); + LOG(info) << "New flat gas number per orbit: " << mFlatGasNumber; + } +} + +} // namespace eventgen +} // namespace o2 \ No newline at end of file diff --git a/Generators/src/TPCLoopersParam.cxx b/Generators/src/TPCLoopersParam.cxx new file mode 100644 index 0000000000000..0202a8ced0535 --- /dev/null +++ b/Generators/src/TPCLoopersParam.cxx @@ -0,0 +1,15 @@ +// Copyright 2024-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. + +/// \author M+Giacalone - September 2025 + +#include "Generators/TPCLoopersParam.h" +O2ParamImpl(o2::eventgen::GenTPCLoopersParam); diff --git a/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx index 28f259c11162b..8197b1be1847b 100644 --- a/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx @@ -11,6 +11,7 @@ #include "FV0DigitizerSpec.h" #include "DataFormatsFV0/ChannelData.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "DataFormatsFV0/Digit.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" @@ -30,6 +31,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "DetectorsBase/BaseDPLDigitizer.h" #include "DetectorsRaw/HBFUtils.h" +#include "Framework/CCDBParamSpec.h" #include using namespace o2::framework; @@ -53,6 +55,16 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer LOG(debug) << "FV0DPLDigitizerTask:init"; mDigitizer.init(); mDisableQED = ic.options().get("disable-qed"); //TODO: QED implementation to be tested + mUseDeadChannelMap = !ic.options().get("disable-dead-channel-map"); + mUpdateDeadChannelMap = mUseDeadChannelMap; + } + + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) + { + // Initialize the dead channel map only once + if (matcher == ConcreteDataMatcher("FV0", "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + } } void run(framework::ProcessingContext& pc) @@ -67,6 +79,11 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer context->initSimChains(o2::detectors::DetID::FV0, mSimChains); const bool withQED = context->isQEDProvided() && !mDisableQED; //TODO: QED implementation to be tested + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + auto deadChannelMap = pc.inputs().get("fv0deadchannelmap"); + mDigitizer.setDeadChannelMap(deadChannelMap.get()); + } + mDigitizer.setTimeStamp(context->getGRP().getTimeStart()); auto& irecords = context->getEventRecords(withQED); //TODO: QED implementation to be tested @@ -131,6 +148,8 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer private: bool mFinished = false; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; Digitizer mDigitizer; std::vector mSimChains; std::vector mDigitsCh; @@ -159,16 +178,19 @@ o2::framework::DataProcessorSpec getFV0DigitizerSpec(int channel, bool mctruth) } outputs.emplace_back("FV0", "ROMode", 0, Lifetime::Timeframe); + std::vector inputs; + inputs.emplace_back("fv0deadchannelmap", "FV0", "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/DeadChannelMap")); + inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); return DataProcessorSpec{ "FV0Digitizer", - Inputs{InputSpec{"collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe}}, - + inputs, outputs, AlgorithmSpec{adaptFromTask()}, Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}, - {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; - //Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}}}; + {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"Don't mask dead channels"}}}}; + // Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}}}; } } // end namespace fv0 diff --git a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx index af0af091d40e8..639203bdd6d38 100644 --- a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx @@ -254,6 +254,11 @@ class ITS3DPLDigitizerTask : BaseDPLDigitizer pc.inputs().get("IT3_dead"); // trigger final ccdb update } + pc.inputs().get("IT3_alpiderespvbb0"); + if (o2::its3::ITS3Params::Instance().chipResponseFunction != "Alpide") { + pc.inputs().get("IT3_aptsresp"); + } + // init digitizer mDigitizer.init(); } @@ -273,6 +278,14 @@ class ITS3DPLDigitizerTask : BaseDPLDigitizer mDigitizer.setDeadChannelsMap((o2::itsmft::NoiseMap*)obj); return; } + if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDERESPVbb0", 0)) { + LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=0V"; + mDigitizer.getParams().setOBSimResponse((o2::itsmft::AlpideSimResponse*)obj); + } + if (matcher == ConcreteDataMatcher(mOrigin, "APTSRESP", 0)) { + LOG(info) << mID.getName() << " loaded APTSResponseData"; + mDigitizer.getParams().setIBSimResponse((o2::itsmft::AlpideSimResponse*)obj); + } } private: @@ -306,6 +319,8 @@ DataProcessorSpec getITS3DigitizerSpec(int channel, bool mctruth) if (o2::its3::ITS3Params::Instance().useDeadChannelMap) { inputs.emplace_back("IT3_dead", "IT3", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("IT3/Calib/DeadMap")); } + inputs.emplace_back("IT3_alpiderespvbb0", "IT3", "ALPIDERESPVbb0", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbb0")); + inputs.emplace_back("IT3_aptsresp", "IT3", "APTSRESP", 0, Lifetime::Condition, ccdbParamSpec("IT3/Calib/APTSResponse")); return DataProcessorSpec{detStr + "Digitizer", inputs, makeOutChannels(detOrig, mctruth), diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index 72ec65b2e522b..eafb72c675a58 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -17,12 +17,13 @@ #include "Framework/Lifetime.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" -#include "Steer/HitProcessingManager.h" // for DigitizationContext +#include "SimulationDataFormat/DigitizationContext.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/NoiseMap.h" #include "DataFormatsITSMFT/TimeDeadMap.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DetectorsBase/BaseDPLDigitizer.h" +#include "DetectorsRaw/HBFUtils.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" @@ -36,20 +37,25 @@ #include #include #include +#include using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { using namespace o2::base; +template class ITSMFTDPLDigitizerTask : BaseDPLDigitizer { 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()}; + using BaseDPLDigitizer::init; + void initDigitizerTask(framework::InitContext& ic) override { mDisableQED = ic.options().get("disable-qed"); @@ -60,121 +66,179 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer if (mFinished) { return; } + mFirstOrbitTF = pc.services().get().firstTForbit; - mID == o2::detectors::DetID::ITS ? updateTimeDependentParams(pc) : updateTimeDependentParams(pc); - std::string detStr = mID.getName(); + const o2::InteractionRecord firstIR(0, mFirstOrbitTF); + updateTimeDependentParams(pc); + + TStopwatch timer; + timer.Start(); + LOG(info) << " CALLING ITS DIGITIZATION "; + // read collision context from input auto context = pc.inputs().get("collisioncontext"); - context->initSimChains(mID, mSimChains); + context->initSimChains(ID, mSimChains); const bool withQED = context->isQEDProvided() && !mDisableQED; auto& timesview = context->getEventRecords(withQED); LOG(info) << "GOT " << timesview.size() << " COLLISSION TIMES"; - LOG(info) << "SIMCHAINS " << mSimChains.size(); + LOG(info) << "SIMCHAINS: " << mSimChains.size(); // if there is nothing to do ... return if (timesview.size() == 0) { return; } - TStopwatch timer; - timer.Start(); - LOG(info) << " CALLING ITS DIGITIZATION "; - mDigitizer.setDigits(&mDigits); - mDigitizer.setROFRecords(&mROFRecords); - mDigitizer.setMCLabels(&mLabels); + uint64_t nDigits{0}; + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? NLayers : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + const int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + mDigitizer.setDigits(&mDigits[iLayer]); + mDigitizer.setROFRecords(&mROFRecords[iLayer]); + mDigitizer.setMCLabels(&mLabels[iLayer]); + mDigitizer.resetROFrameBounds(); + + // digits are directly put into DPL owned resource + auto& digitsAccum = pc.outputs().make>(Output{Origin, "DIGITS", iLayer}); + + // rofs are accumulated first and the copied + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / DPLAlpideParam::Instance().getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * raw::HBFUtils::Instance().getNOrbitsPerTF(); + mROFRecordsAccum[iLayer].reserve(nROFsTF); + + auto accumulate = [this, &digitsAccum, &iLayer]() { + // accumulate result of single event processing on a specific layer, called after processing every event supplied + // AND after the final flushing via digitizer::fillOutputContainer + if (!mDigits[iLayer].size()) { + return; // no digits were flushed, nothing to accumulate + } + auto ndigAcc = digitsAccum.size(); + std::copy(mDigits[iLayer].begin(), mDigits[iLayer].end(), std::back_inserter(digitsAccum)); + + // fix ROFrecords references on ROF entries + auto nROFRecsOld = mROFRecordsAccum[iLayer].size(); + + for (int i = 0; i < mROFRecords[iLayer].size(); i++) { + auto& rof = mROFRecords[iLayer][i]; + rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); + rof.print(); + + if (mFixMC2ROF[iLayer] < mMC2ROFRecordsAccum[iLayer].size()) { // fix ROFRecord entry in MC2ROF records + for (int m2rid = mFixMC2ROF[iLayer]; m2rid < mMC2ROFRecordsAccum[iLayer].size(); m2rid++) { + // need to register the ROFRecors entry for MC event starting from this entry + auto& mc2rof = mMC2ROFRecordsAccum[iLayer][m2rid]; + if (rof.getROFrame() == mc2rof.minROF) { + mFixMC2ROF[iLayer]++; + mc2rof.rofRecordID = nROFRecsOld + i; + mc2rof.print(); + } + } + } + } + + std::copy(mROFRecords[iLayer].begin(), mROFRecords[iLayer].end(), std::back_inserter(mROFRecordsAccum[iLayer])); + if (mWithMCTruth) { + mLabelsAccum[iLayer].mergeAtBack(mLabels[iLayer]); + } + LOG(info) << "Added " << mDigits[iLayer].size() << " digits:" << iLayer; + // clean containers from already accumulated stuff + mLabels[iLayer].clear(); + mDigits[iLayer].clear(); + mROFRecords[iLayer].clear(); + }; // and accumulate lambda + + const auto& eventParts = context->getEventParts(withQED); + const int64_t bcShift = mDigitizer.getParams().getROFrameBiasInBC(layer); // this accounts the misalignment and the opt. imposed rof delay + // loop over all composite collisions given from context (aka loop over all the interaction records) + for (int collID = 0; collID < timesview.size(); ++collID) { + auto irt = timesview[collID]; + if (irt.toLong() < bcShift) { // due to the ROF misalignment (+opt. delay) the collision would go to negative ROF ID, discard + continue; + } + irt -= bcShift; // account for the ROF start shift - // digits are directly put into DPL owned resource - auto& digitsAccum = pc.outputs().make>(Output{mOrigin, "DIGITS", 0}); + mDigitizer.setEventTime(irt, layer); + mDigitizer.resetEventROFrames(); // to estimate min/max ROF for this collID + // for each collision, loop over the constituents event and source IDs + // (background signal merging is basically taking place here) + for (const auto& part : eventParts[collID]) { - auto accumulate = [this, &digitsAccum]() { - // accumulate result of single event processing, called after processing every event supplied - // AND after the final flushing via digitizer::fillOutputContainer - if (!mDigits.size()) { - return; // no digits were flushed, nothing to accumulate + // get the hits for this event and this source + mHits.clear(); + context->retrieveHits(mSimChains, o2::detectors::SimTraits::DETECTORBRANCHNAMES[ID][0].c_str(), part.sourceID, part.entryID, &mHits); + + if (mHits.size() > 0) { + LOG(debug) << "For collision " << collID << " eventID " << part.entryID << " found " << mHits.size() << " hits "; + mDigitizer.process(&mHits, part.entryID, part.sourceID, layer); // call actual digitization procedure + } + } + mMC2ROFRecordsAccum[iLayer].emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax()); + accumulate(); } - auto ndigAcc = digitsAccum.size(); - std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(digitsAccum)); - - // fix ROFrecords references on ROF entries - auto nROFRecsOld = mROFRecordsAccum.size(); - - for (int i = 0; i < mROFRecords.size(); i++) { - auto& rof = mROFRecords[i]; - rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); - rof.print(); - - if (mFixMC2ROF < mMC2ROFRecordsAccum.size()) { // fix ROFRecord entry in MC2ROF records - for (int m2rid = mFixMC2ROF; m2rid < mMC2ROFRecordsAccum.size(); m2rid++) { - // need to register the ROFRecors entry for MC event starting from this entry - auto& mc2rof = mMC2ROFRecordsAccum[m2rid]; - if (rof.getROFrame() == mc2rof.minROF) { - mFixMC2ROF++; - mc2rof.rofRecordID = nROFRecsOld + i; - mc2rof.print(); - } + mDigitizer.fillOutputContainer(0xffffffff, layer); + accumulate(); + nDigits += digitsAccum.size(); + + // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) + // ensure that the rof output is continuous + if (nROFsTF != mROFRecordsAccum[iLayer].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 + // also have to account for spillage into next TF + const size_t nROFsLayer = std::max((size_t)nROFsTF, mROFRecordsAccum[iLayer].size()); + std::vector expDigitRofVec(nROFsLayer); + for (int iROF{0}; iROF < nROFsLayer; ++iROF) { + auto& rof = expDigitRofVec[iROF]; + int orb = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF; + int bc = iROF * DPLAlpideParam::Instance().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 : mROFRecordsAccum[iLayer]) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const int irROF = irToFirst.toLong() / DPLAlpideParam::Instance().getROFLengthInBC(iLayer); + auto& expROF = expDigitRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected {} and received {}", expROF.asString(), rof.asString()); } } + int prevFirst{0}; + for (auto& rof : expDigitRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + // if more rofs where accumulated than ROFs possible in the TF, cut them away + // by construction expDigitRofVec is at least nROFsTF long + expDigitRofVec.resize(nROFsTF); + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, expDigitRofVec); + } else { + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, mROFRecordsAccum[iLayer]); } - - std::copy(mROFRecords.begin(), mROFRecords.end(), std::back_inserter(mROFRecordsAccum)); if (mWithMCTruth) { - mLabelsAccum.mergeAtBack(mLabels); - } - LOG(info) << "Added " << mDigits.size() << " digits "; - // clean containers from already accumulated stuff - mLabels.clear(); - mDigits.clear(); - mROFRecords.clear(); - }; // and accumulate lambda - - auto& eventParts = context->getEventParts(withQED); - int bcShift = mDigitizer.getParams().getROFrameBiasInBC(); - // loop over all composite collisions given from context (aka loop over all the interaction records) - for (int collID = 0; collID < timesview.size(); ++collID) { - auto irt = timesview[collID]; - if (irt.toLong() < bcShift) { // due to the ROF misalignment the collision would go to negative ROF ID, discard - continue; - } - irt -= bcShift; // account for the ROF start shift - - mDigitizer.setEventTime(irt); - mDigitizer.resetEventROFrames(); // to estimate min/max ROF for this collID - // for each collision, loop over the constituents event and source IDs - // (background signal merging is basically taking place here) - for (auto& part : eventParts[collID]) { - - // get the hits for this event and this source - mHits.clear(); - context->retrieveHits(mSimChains, o2::detectors::SimTraits::DETECTORBRANCHNAMES[mID][0].c_str(), part.sourceID, part.entryID, &mHits); - - if (mHits.size() > 0) { - LOG(debug) << "For collision " << collID << " eventID " << part.entryID - << " found " << mHits.size() << " hits "; - mDigitizer.process(&mHits, part.entryID, part.sourceID); // call actual digitization procedure - } + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, mMC2ROFRecordsAccum[iLayer]); + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", iLayer}); + mLabelsAccum[iLayer].flatten_to(sharedlabels); + // free space of existing label containers + mLabels[iLayer].clear_andfreememory(); + mLabelsAccum[iLayer].clear_andfreememory(); } - mMC2ROFRecordsAccum.emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax()); - accumulate(); - } - mDigitizer.fillOutputContainer(); - accumulate(); - - // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) - - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mROFRecordsAccum); - if (mWithMCTruth) { - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mMC2ROFRecordsAccum); - auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); - mLabelsAccum.flatten_to(sharedlabels); - // free space of existing label containers - mLabels.clear_andfreememory(); - mLabelsAccum.clear_andfreememory(); } - LOG(info) << mID.getName() << ": Sending ROMode= " << mROMode << " to GRPUpdater"; - pc.outputs().snapshot(Output{mOrigin, "ROMode", 0}, mROMode); + + LOG(info) << ID.getName() << ": Sending ROMode= " << mROMode << " to GRPUpdater"; + pc.outputs().snapshot(Output{Origin, "ROMode", 0}, mROMode); timer.Stop(); LOG(info) << "Digitization took " << timer.CpuTime() << "s"; + LOG(info) << "Produced " << nDigits << " digits"; // we should be only called once; tell DPL that this process is ready to exit pc.services().get().readyToQuit(QuitRequest::Me); @@ -184,18 +248,18 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(mOrigin, "NOISEMAP", 0)) { - LOG(info) << mID.getName() << " noise map updated"; + if (matcher == ConcreteDataMatcher(Origin, "NOISEMAP", 0)) { + LOG(info) << ID.getName() << " noise map updated"; mDigitizer.setNoiseMap((const o2::itsmft::NoiseMap*)obj); return; } - if (matcher == ConcreteDataMatcher(mOrigin, "DEADMAP", 0)) { - LOG(info) << mID.getName() << " static dead map updated"; + if (matcher == ConcreteDataMatcher(Origin, "DEADMAP", 0)) { + LOG(info) << ID.getName() << " static dead map updated"; mDeadMap = (o2::itsmft::NoiseMap*)obj; mDigitizer.setDeadChannelsMap(mDeadMap); return; } - if (matcher == ConcreteDataMatcher(mOrigin, "TimeDeadMap", 0)) { + if (matcher == ConcreteDataMatcher(Origin, "TimeDeadMap", 0)) { o2::itsmft::TimeDeadMap* timedeadmap = (o2::itsmft::TimeDeadMap*)obj; if (!timedeadmap->isDefault()) { timedeadmap->decodeMap(mFirstOrbitTF, *mDeadMap, true); @@ -204,41 +268,45 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } mTimeDeadMapUpdated = true; mDigitizer.setDeadChannelsMap(mDeadMap); - LOG(info) << mID.getName() << " time-dependent dead map updated"; + LOG(info) << ID.getName() << " time-dependent dead map updated"; } else { - LOG(info) << mID.getName() << " time-dependent dead map is default/empty"; + LOG(info) << ID.getName() << " time-dependent dead map is default/empty"; } return; } - if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDEPARAM", 0)) { - LOG(info) << mID.getName() << " Alpide param updated"; - if (mID == o2::detectors::DetID::ITS) { - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - } else { - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - } + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << ID.getName() << " Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); return; } + if (matcher == ConcreteDataMatcher(Origin, "ALPIDERESPVbb0", 0)) { + LOG(info) << ID.getName() << " loaded AlpideResponseData for Vbb=0V"; + mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 0); + } + if (matcher == ConcreteDataMatcher(Origin, "ALPIDERESPVbbM3", 0)) { + LOG(info) << ID.getName() << " loaded AlpideResponseData for Vbb=-3V"; + mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 1); + } } protected: ITSMFTDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth) {} - template void updateTimeDependentParams(ProcessingContext& pc) { - std::string detstr(o2::detectors::DetID::getName(DETID)); + std::string detstr(o2::detectors::DetID::getName(ID)); pc.inputs().get(detstr + "_noise"); pc.inputs().get(detstr + "_dead"); // TODO: the code should run even if this object does not exist. Or: create default object pc.inputs().get(detstr + "_time_dead"); - pc.inputs().get*>(detstr + "_alppar"); + pc.inputs().get*>(detstr + "_alppar"); + pc.inputs().get(detstr + "_alpiderespvbb0"); + pc.inputs().get(detstr + "_alpiderespvbbm3"); - auto& dopt = o2::itsmft::DPLDigitizerParam::Instance(); - auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); + auto& dopt = o2::itsmft::DPLDigitizerParam::Instance(); + auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); auto& digipar = mDigitizer.getParams(); digipar.setContinuous(dopt.continuous); digipar.setROFrameBiasInBC(aopt.roFrameBiasInBC); @@ -262,15 +330,29 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer digipar.setIBVbb(dopt.IBVbb); digipar.setOBVbb(dopt.OBVbb); digipar.setVbb(dopt.Vbb); + // staggering parameters + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + const bool withStag = aopt.withStaggering(); + for (int iLayer{0}; iLayer < o2::itsmft::DPLAlpideParam::getNLayers(); ++iLayer) { + const int nLayer = (withStag) ? iLayer : -1; + auto frameNS = aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS; + digipar.addROFrameLayerLengthInBC(aopt.getROFLengthInBC(nLayer)); + // NOTE: the rof delay looks from the digitizer like an additional bias + digipar.addROFrameLayerBiasInBC(aopt.getROFBiasInBC(nLayer) + aopt.getROFDelayInBC(nLayer)); + digipar.addStrobeDelay(aopt.strobeDelay); + digipar.addStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); + digipar.setROFrameLength(aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS, iLayer); + } + } mROMode = digipar.isContinuous() ? o2::parameters::GRPObject::CONTINUOUS : o2::parameters::GRPObject::PRESENT; - LOG(info) << mID.getName() << " simulated in " + LOG(info) << detstr << " simulated in " << ((mROMode == o2::parameters::GRPObject::CONTINUOUS) ? "CONTINUOUS" : "TRIGGERED") << " RO mode"; // configure digitizer o2::itsmft::GeometryTGeo* geom = nullptr; - if (mID == o2::detectors::DetID::ITS) { + if constexpr (N == o2::detectors::DetID::ITS) { geom = o2::its::GeometryTGeo::Instance(); } else { geom = o2::mft::GeometryTGeo::Instance(); @@ -284,115 +366,95 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer bool mFinished = false; bool mDisableQED = false; unsigned long mFirstOrbitTF = 0x0; - o2::detectors::DetID mID; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; o2::itsmft::Digitizer mDigitizer; - std::vector mDigits; - std::vector mROFRecords; - std::vector mROFRecordsAccum; + std::array, NLayers> mDigits; + std::array, NLayers> mROFRecords; + std::array, NLayers> mROFRecordsAccum; std::vector mHits; std::vector* mHitsP = &mHits; - o2::dataformats::MCTruthContainer mLabels; - o2::dataformats::MCTruthContainer mLabelsAccum; - std::vector mMC2ROFRecordsAccum; + std::array, NLayers> mLabels; + std::array, NLayers> mLabelsAccum; + std::array, NLayers> mMC2ROFRecordsAccum; std::vector mSimChains; o2::itsmft::NoiseMap* mDeadMap = nullptr; - int mFixMC2ROF = 0; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID + std::array mFixMC2ROF{}; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID bool mTimeDeadMapUpdated = false; o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::PRESENT; // readout mode }; //_______________________________________________ -class ITSDPLDigitizerTask : public ITSMFTDPLDigitizerTask +class ITSDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - // FIXME: origin should be extractable from the DetID, the problem is 3d party header dependencies - static constexpr o2::detectors::DetID::ID DETID = o2::detectors::DetID::ITS; - static constexpr o2::header::DataOrigin DETOR = o2::header::gDataOriginITS; - ITSDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) - { - mID = DETID; - mOrigin = DETOR; - } + ITSDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} }; -constexpr o2::detectors::DetID::ID ITSDPLDigitizerTask::DETID; -constexpr o2::header::DataOrigin ITSDPLDigitizerTask::DETOR; - //_______________________________________________ -class MFTDPLDigitizerTask : public ITSMFTDPLDigitizerTask +class MFTDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - // FIXME: origina should be extractable from the DetID, the problem is 3d party header dependencies - static constexpr o2::detectors::DetID::ID DETID = o2::detectors::DetID::MFT; - static constexpr o2::header::DataOrigin DETOR = o2::header::gDataOriginMFT; - MFTDPLDigitizerTask(bool mctruth) : ITSMFTDPLDigitizerTask(mctruth) - { - mID = DETID; - mOrigin = DETOR; - } + MFTDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} }; -constexpr o2::detectors::DetID::ID MFTDPLDigitizerTask::DETID; -constexpr o2::header::DataOrigin MFTDPLDigitizerTask::DETOR; - +namespace +{ +template std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth) { std::vector outputs; - outputs.emplace_back(detOrig, "DIGITS", 0, Lifetime::Timeframe); - outputs.emplace_back(detOrig, "DIGITSROF", 0, Lifetime::Timeframe); - if (mctruth) { - outputs.emplace_back(detOrig, "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back(detOrig, "DIGITSMCTR", 0, Lifetime::Timeframe); + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + outputs.emplace_back(detOrig, "DIGITS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mctruth) { + outputs.emplace_back(detOrig, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + } } outputs.emplace_back(detOrig, "ROMode", 0, Lifetime::Timeframe); return outputs; } +} // namespace DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) { - std::string detStr = o2::detectors::DetID::getName(ITSDPLDigitizerTask::DETID); - auto detOrig = ITSDPLDigitizerTask::DETOR; - std::stringstream parHelper; - parHelper << "Params as " << o2::itsmft::DPLDigitizerParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLDigitizerParam::Instance() - << "\n or " << o2::itsmft::DPLAlpideParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLAlpideParam::Instance(); + std::string detStr = o2::detectors::DetID::getName(ITSDPLDigitizerTask::ID); + auto detOrig = ITSDPLDigitizerTask::Origin; std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); inputs.emplace_back("ITS_noise", "ITS", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/NoiseMap")); inputs.emplace_back("ITS_dead", "ITS", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/DeadMap")); inputs.emplace_back("ITS_time_dead", "ITS", "TimeDeadMap", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/TimeDeadMap")); inputs.emplace_back("ITS_alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); - - return DataProcessorSpec{(detStr + "Digitizer").c_str(), - inputs, makeOutChannels(detOrig, mctruth), - AlgorithmSpec{adaptFromTask(mctruth)}, - Options{ + inputs.emplace_back("ITS_alpiderespvbb0", "ITS", "ALPIDERESPVbb0", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbb0")); + inputs.emplace_back("ITS_alpiderespvbbm3", "ITS", "ALPIDERESPVbbM3", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbbM3")); + return DataProcessorSpec{.name = detStr + "Digitizer", + .inputs = inputs, + .outputs = makeOutChannels(detOrig, mctruth), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth)}, + .options = Options{ {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) { - std::string detStr = o2::detectors::DetID::getName(MFTDPLDigitizerTask::DETID); - auto detOrig = MFTDPLDigitizerTask::DETOR; - std::stringstream parHelper; + std::string detStr = o2::detectors::DetID::getName(MFTDPLDigitizerTask::ID); + auto detOrig = MFTDPLDigitizerTask::Origin; std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); inputs.emplace_back("MFT_noise", "MFT", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/NoiseMap")); inputs.emplace_back("MFT_dead", "MFT", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/DeadMap")); inputs.emplace_back("MFT_time_dead", "MFT", "TimeDeadMap", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/TimeDeadMap")); inputs.emplace_back("MFT_alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); - parHelper << "Params as " << o2::itsmft::DPLDigitizerParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLDigitizerParam::Instance() - << " or " << o2::itsmft::DPLAlpideParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLAlpideParam::Instance(); - return DataProcessorSpec{(detStr + "Digitizer").c_str(), - inputs, makeOutChannels(detOrig, mctruth), - AlgorithmSpec{adaptFromTask(mctruth)}, - Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; + inputs.emplace_back("MFT_alpiderespvbb0", "MFT", "ALPIDERESPVbb0", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbb0")); + inputs.emplace_back("MFT_alpiderespvbbm3", "MFT", "ALPIDERESPVbbM3", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbbM3")); + return DataProcessorSpec{.name = detStr + "Digitizer", + .inputs = inputs, + .outputs = makeOutChannels(detOrig, mctruth), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth)}, + .options = Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } -} // end namespace itsmft -} // end namespace o2 +} // namespace o2::itsmft + // end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index ea5c6ba272ec6..c45c746064101 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -32,7 +32,7 @@ #include "TPCDigitizerSpec.h" #include "TPCSimWorkflow/TPCDigitRootWriterSpec.h" #include "TPCBase/Sector.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" // needed in order to init the **SHARED** polyadist file (to be done before the digitizers initialize) #include "TPCSimulation/GEMAmplification.h" diff --git a/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx index 381e1ecdd3e91..68476c3a92a6d 100644 --- a/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx @@ -33,7 +33,7 @@ #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsTPC/TPCSectorHeader.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBaseRecSim/CDBInterface.h" #include "TPCBase/ParameterGEM.h" #include "DataFormatsTPC/Digit.h" #include "TPCSimulation/Digitizer.h" diff --git a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx index 0ed276237bd86..30f9d33983712 100644 --- a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx @@ -34,6 +34,7 @@ #include #include +#include #include using namespace o2::framework; @@ -68,6 +69,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer void initDigitizerTask(framework::InitContext& ic) override { mDisableQED = ic.options().get("disable-qed"); + mLocalRespFile = ic.options().get("local-response-file"); } void run(framework::ProcessingContext& pc) @@ -200,6 +202,15 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer mFinished = true; } + void setLocalResponseFunction() + { + std::unique_ptr file(TFile::Open(mLocalRespFile.data(), "READ")); + if (!file) { + LOG(fatal) << "Cannot open response file " << mLocalRespFile; + } + mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1")); + } + void updateTimeDependentParams(ProcessingContext& pc) { static bool initOnce{false}; @@ -244,6 +255,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // if (oTRKParams::Instance().useDeadChannelMap) { // pc.inputs().get("TRK_dead"); // trigger final ccdb update // } + pc.inputs().get("TRK_aptsresp"); // init digitizer mDigitizer.init(); @@ -264,20 +276,33 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // mDigitizer.setDeadChannelsMap((o2::itsmft::NoiseMap*)obj); // return; // } + if (matcher == ConcreteDataMatcher(mOrigin, "APTSRESP", 0)) { + LOG(info) << mID.getName() << " loaded APTSResponseData"; + if (mLocalRespFile.empty()) { + LOG(info) << "Using CCDB/APTS response file"; + mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)obj); + mDigitizer.setResponseName("APTS"); + } else { + LOG(info) << "Response function will be loaded from local file: " << mLocalRespFile; + setLocalResponseFunction(); + mDigitizer.setResponseName("ALICE3"); + } + } } private: bool mWithMCTruth{true}; bool mFinished{false}; bool mDisableQED{false}; + std::string mLocalRespFile{""}; const o2::detectors::DetID mID{o2::detectors::DetID::TRK}; const o2::header::DataOrigin mOrigin{o2::header::gDataOriginTRK}; o2::trk::Digitizer mDigitizer{}; std::vector mDigits{}; std::vector mROFRecords{}; std::vector mROFRecordsAccum{}; - std::vector mHits{}; - std::vector* mHitsP{&mHits}; + std::vector mHits{}; + std::vector* mHitsP{&mHits}; o2::dataformats::MCTruthContainer mLabels{}; o2::dataformats::MCTruthContainer mLabelsAccum{}; std::vector mMC2ROFRecordsAccum{}; @@ -297,11 +322,14 @@ DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth) // if (oTRKParams::Instance().useDeadChannelMap) { // inputs.emplace_back("TRK_dead", "TRK", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("TRK/Calib/DeadMap")); // } + inputs.emplace_back("TRK_aptsresp", "TRK", "APTSRESP", 0, Lifetime::Condition, ccdbParamSpec("IT3/Calib/APTSResponse")); return DataProcessorSpec{detStr + "Digitizer", inputs, makeOutChannels(detOrig, mctruth), AlgorithmSpec{adaptFromTask(mctruth)}, - Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; + Options{ + {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}, + {"local-response-file", o2::framework::VariantType::String, "", {"use response file saved locally at this path/filename"}}}}; } } // namespace o2::trk diff --git a/Steer/include/Steer/O2MCApplicationBase.h b/Steer/include/Steer/O2MCApplicationBase.h index 36966be9bde62..d61199baba0ae 100644 --- a/Steer/include/Steer/O2MCApplicationBase.h +++ b/Steer/include/Steer/O2MCApplicationBase.h @@ -58,6 +58,8 @@ class O2MCApplicationBase : public FairMCApplication typedef std::function TrackRefFcn; + void fixTGeoRuntimeShapes(); + protected: o2::conf::SimCutParams const& mCutParams; // reference to parameter system unsigned long long mStepCounter{0}; diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index 9fc3e548ff213..6bee407c01264 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -28,6 +28,10 @@ #include #include "DataFormatsParameters/GRPLHCIFData.h" #include "SimConfig/SimConfig.h" +#include +#include +#include +#include // // Created by Sandro Wenzel on 13.07.21. @@ -59,6 +63,12 @@ struct Options { // format is path prefix std::string vertexModeString{"kNoVertex"}; // Vertex Mode; vertices will be assigned to collisions of mode != kNoVertex o2::conf::VertexMode vertexMode = o2::conf::VertexMode::kNoVertex; + std::string external_path = ""; // optional external path where we can directly take the collision contexts + // This is useful when someone else is creating the contexts (MC-data embedding) and we + // merely want to pass these through. If this is given, we simply take the timeframe ID, number of orbits + // and copy the right amount of timeframes into the destination folder (implies individualTFextraction) + std::string nontrivial_mu_distribution = ""; // path to fetch a non-uniform MC(BC) distribution for the interaction sampler + // can be: (a) ccdb, (b) a ROOT file with the histogram included }; enum class InteractionLockMode { @@ -67,6 +77,28 @@ enum class InteractionLockMode { MINTIMEDISTANCE }; +struct CcdbUrl { + std::string server; // may include http:// or https:// + std::string port; // empty if none + std::string fullPath; // everything after server[:port]/ +}; + +std::optional parseCcdbRegex(const std::string& url) +{ + static const std::regex re( + R"(^(?:ccdb://)(https?://[^/:]+|[^/:]+)(?::(\d+))?/(.+)$)"); + std::smatch m; + if (!std::regex_match(url, m, re)) { + return std::nullopt; + } + + CcdbUrl out; + out.server = m[1].str(); // server (may include http:// or https://) + out.port = m[2].str(); // optional port + out.fullPath = m[3].str(); // remainder + return out; +} + struct InteractionSpec { std::string name; // name (prefix for transport simulation); may also serve as unique identifier float interactionRate; @@ -210,7 +242,9 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "with-vertices", bpo::value(&optvalues.vertexModeString)->default_value("kNoVertex"), "Assign vertices to collisions. Argument is the vertex mode. Defaults to no vertexing applied")( "timestamp", bpo::value(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring")( "extract-per-timeframe", bpo::value(&optvalues.individualTFextraction)->default_value(""), - "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]"); + "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]")( + "import-external", bpo::value(&optvalues.external_path)->default_value(""), "Take collision contexts (per timeframe) from external files for instance for data-anchoring use-case. Needs timeframeID and number of orbits to be given as well.")( + "nontrivial-mu-distribution", bpo::value(&optvalues.nontrivial_mu_distribution)->default_value(""), "Distribution for MU(BC)"); options.add_options()("help,h", "Produce help message."); @@ -249,6 +283,54 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) return true; } +bool copy_collision_context(const std::string& external_path, int this_tf_id, int target_tf_id) +{ + namespace fs = std::filesystem; + try { + fs::path filename; + if (fs::exists(external_path) && fs::is_regular_file(external_path)) { + std::cout << "external_path is an existing file: " << external_path << "\n"; + // use it directly + filename = fs::path(external_path); + } else { + // Construct source file path + filename = fs::path(external_path) / ("collission_context_" + std::to_string(this_tf_id) + ".root"); + } + + LOG(info) << "Checking existence of file: " << filename; + + if (fs::exists(filename)) { + // Build destination path + std::string path_prefix = "tf"; // Can be made configurable + std::stringstream destination_path_stream; + destination_path_stream << path_prefix << (target_tf_id) << "/collisioncontext.root"; + fs::path destination_path = destination_path_stream.str(); + + // Ensure parent directory exists + fs::path destination_dir = destination_path.parent_path(); + if (!fs::exists(destination_dir)) { + fs::create_directories(destination_dir); + LOG(info) << "Created directory: " << destination_dir; + } + + // Copy file + fs::copy_file(filename, destination_path, fs::copy_options::overwrite_existing); + LOG(info) << "Copied file to: " << destination_path; + return true; + } else { + LOG(warning) << "Source file does not exist: " << filename; + return false; + } + } catch (const fs::filesystem_error& e) { + LOG(error) << "Filesystem error: " << e.what(); + return false; + } catch (const std::exception& e) { + LOG(error) << "Unexpected error: " << e.what(); + return false; + } + return true; +} + int main(int argc, char* argv[]) { Options options; @@ -259,6 +341,45 @@ int main(int argc, char* argv[]) // init params o2::conf::ConfigurableParam::updateFromString(options.configKeyValues); + // See if this is external mode, which simplifies things + if (options.external_path.size() > 0) { + // in this mode, we don't actually have to do much work. + // all we do is to + // - determine how many timeframes are asked + // - check if the right files are present in the external path (someone else needs to create/put them there) + // - check if the given contexts are consistent with options given (orbitsPerTF, ...) + // - copy the files into the MC destination folder (this implies timeframeextraction mode) + // - return + + if (options.orbits < 0) { + LOG(error) << "External mode; orbits need to be given"; + return 1; + } + + if (options.orbitsPerTF == 0) { + LOG(error) << "External mode; need to have orbitsPerTF"; + return 1; + } + + if (options.individualTFextraction.size() == 0) { + LOG(error) << "External mode: This requires --extract-per-timeframe"; + return 1; + } + + // calculate number of timeframes + auto num_timeframes = options.orbits / options.orbitsPerTF; + LOG(info) << "External mode for " << num_timeframes << " consecutive timeframes; starting from " << options.tfid; + + // loop over all timeframe ids - check if file is present - (check consistency) - copy to final destination + for (int i = 0; i < num_timeframes; ++i) { + auto this_tf_id = options.tfid + i; + if (!copy_collision_context(options.external_path, this_tf_id, i + 1)) { + return 1; + } + } + return 0; + } + // init random generator gRandom->SetSeed(options.seed); @@ -303,6 +424,46 @@ int main(int argc, char* argv[]) auto mode = ispecs[id].syncmode; if (mode == InteractionLockMode::NOLOCK) { auto sampler = std::make_unique(); + TH1F* mu_hist = nullptr; + + // we check if there is a realistic bunch crossing distribution available + const auto& mu_distr_source = options.nontrivial_mu_distribution; + if (mu_distr_source.size() > 0) { + if (mu_distr_source.find("ccdb") == 0) { + auto ccdb_info_wrapper = parseCcdbRegex(mu_distr_source); + if (!ccdb_info_wrapper.has_value()) { + LOG(error) << "Could not parse CCDB path for mu(bc) distribution"; + } else { + auto& ccdb_info = ccdb_info_wrapper.value(); + + // for now construct a specific CCDBManager for this query + o2::ccdb::CCDBManagerInstance ccdb_inst(ccdb_info.server + std::string(":") + ccdb_info.port); + ccdb_inst.setFatalWhenNull(false); + auto local_hist = ccdb_inst.getForTimeStamp(ccdb_info.fullPath, options.timestamp); + if (local_hist) { + mu_hist = (TH1F*)(local_hist->Clone("h2")); // we need to clone since ownership of local_hist is with TFile + } else { + LOG(warn) << "No mu(bc) distribution found on CCDB. Using uniform one"; + } + } + } else { + // we interpret the file as a ROOT file and open it to extract the wanted histogram + auto mudistr_file = TFile::Open(mu_distr_source.c_str(), "OPEN"); + if (mudistr_file && !mudistr_file->IsZombie()) { + auto local_hist = mudistr_file->Get("hBcTVX"); + mu_hist = (TH1F*)(local_hist->Clone("h2")); // we need to clone since ownership of local_hist is with TFile + mudistr_file->Close(); + } + } + if (mu_hist) { + LOG(info) << "Found an external mu distribution with mean BC value " << mu_hist->GetMean(); + + // do some checks + + // reset to correct interaction Sampler type + sampler.reset(new o2::steer::NonUniformMuInteractionSampler()); + } + } // for debug purposes: allows to instantiate trivial sampler if (const char* env = getenv("ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER")) { @@ -324,6 +485,13 @@ int main(int argc, char* argv[]) if (!options.bcpatternfile.empty()) { setBCFillingHelper(*sampler, options.bcpatternfile); } + sampler->init(); + if (auto sampler_cast = dynamic_cast(sampler.get())) { + if (mu_hist) { + sampler_cast->setBCIntensityScales(*mu_hist); + } + } + o2::InteractionTimeRecord record; // this loop makes sure that the first collision is within the range of orbits asked (if noEmptyTF is enabled) do { @@ -465,8 +633,18 @@ int main(int argc, char* argv[]) } digicontext.setBunchFilling(bunchFillings[0]); std::vector prefixes; + // Signal interaction rate + float sgnIRate = -1.; for (auto& p : ispecs) { prefixes.push_back(p.name); + // Set the interaction rate from the first pattern with a valid value. + // This handles both simple signal-only productions (where "sgn" has the rate) + // and embedding productions (where "bkg" has the rate and "sgn" syncs to it) + if (sgnIRate < 0 && p.interactionRate > 0) { + LOG(debug) << "Setting signal interaction rate to " << p.interactionRate << " Hz in the digitization context."; + sgnIRate = p.interactionRate; + digicontext.setDigitizerInteractionRate(p.interactionRate); + } } digicontext.setSimPrefixes(prefixes); @@ -478,9 +656,20 @@ int main(int argc, char* argv[]) LOG(info) << "-------- DENSE CONTEXT ------->>"; auto timeframeindices = digicontext.calcTimeframeIndices(orbitstart, options.orbitsPerTF, options.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); + } + // apply max collision per timeframe filters + reindexing of event id (linearisation and compactification) digicontext.applyMaxCollisionFilter(timeframeindices, orbitstart, options.orbitsPerTF, options.maxCollsPerTF, options.orbitsEarly); + LOG(info) << "Timeframe indices after collision filter"; + LOG(info) << "Fixed " << timeframeindices.size() << " timeframes "; + for (auto p : timeframeindices) { + LOG(info) << std::get<0>(p) << " " << std::get<1>(p) << " " << std::get<2>(p); + } + // <---- at this moment we have a dense collision context (not representing the final output we want) LOG(info) << "<<------ FILTERED CONTEXT ---------"; if (options.printContext) { diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 584598d350581..1e3f925042d01 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -37,6 +37,12 @@ #include "SimConfig/GlobalProcessCutSimParam.h" #include "DetectorsBase/GeometryManagerParam.h" #include +#include +#include +#include +#include +#include +#include "SimConfig/G4Params.h" namespace o2 { @@ -209,10 +215,70 @@ bool O2MCApplicationBase::MisalignGeometry() gGeoManager->SetUseParallelWorldNav(true); } + // performs possible optimizations (shape replacements on the runtime geometry) + fixTGeoRuntimeShapes(); + // return original return value of misalignment procedure return true; } +void O2MCApplicationBase::fixTGeoRuntimeShapes() +{ + auto& g4Params = o2::conf::G4Params::Instance(); + if (g4Params.navmode != o2::conf::EG4Nav::kTGeo) { + return; + } + + // Replace TGeo shapes by other ones for performance or other reasons. + // Should only affect runtime of simulation. + + // TODO: make this configurable via external JSON rules/macro + + // Also delete original shapes for memory reasons + + // We follow a visitor pattern on a geom hierarchy + // for now replace a TGeoTessellate by our own implementation + std::unordered_set volumes_visited; + std::unordered_set old_shape_pointers; + + std::function visit; + visit = [&](TGeoNode* node) -> void { + if (!node) { + return; + } + auto vol = node->GetVolume(); + if (volumes_visited.find(vol) != volumes_visited.end()) { + return; + } + volumes_visited.insert(vol); + + // transform the shape of this volume + auto shape = vol->GetShape(); + if (shape->IsA() == TGeoTessellated::Class()) { + auto tsl = static_cast(shape); + + // make a new O2Tessellated until ROOT has proper support for navigation in TGeoTessellated + std::cout << "Converting to O2Tessellated for vol " << vol->GetName() << "\n"; + auto replacement_shape = new o2::base::O2Tessellated(*tsl, false); + vol->SetShape(replacement_shape); + old_shape_pointers.insert(shape); + } + // other cases could come here + + for (int i = 0; i < vol->GetNdaughters(); ++i) { + auto child_node = vol->GetNode(i); + visit(child_node); + } + }; + + visit(gGeoManager->GetTopNode()); + + for (auto ptr : old_shape_pointers) { + delete ptr; + ptr = nullptr; + } +} + void O2MCApplicationBase::finishEventCommon() { LOG(info) << "This event/chunk did " << mStepCounter << " steps"; diff --git a/Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h b/Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h deleted file mode 100644 index 68fcc8360df2b..0000000000000 --- a/Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/* Local Variables: */ -/* mode: c++ */ -/* End: */ - -#ifndef CODINGMODELDISPATCHER_H -#define CODINGMODELDISPATCHER_H - -/// @file CodingModelDispatcher.h -/// @author Matthias Richter -/// @since 2016-09-11 -/// @brief Runtime dispatcher interface for probability model definitions - -#include "mpl_tools.h" -#include "runtime_container.h" -#include -#include -#include -#include - -using namespace gNeric; - -namespace o2 -{ -namespace data_compression -{ - -/** - * @class CodingModelDispatcher Runtime dispatcher interface - * @brief Runtime dispatcher interface for probability model definitions - * - * ModelDefinition single coding model or mpl sequence of models - * - * TODO: - * - consistency check for coding direction, all model definitions have to obey - * same direction - * - probably one should also require the same code type for all definitions, at - * least in the codec. Multiple code types do not make much sense in the codec - */ -template -class CodingModelDispatcher -{ - public: - CodingModelDispatcher() : mPosition(0), mContainer() {} - ~CodingModelDispatcher() = default; - - using self_type = CodingModelDispatcher; - - // make_mpl_vector traits makes sure that an mpl sequence is used further on - // if the original type is not a sequence it is wrapped into an mpl vector with - // the original type as the only element - using definition_type = typename mpl::make_mpl_vector::type; - - // the runtime container type is the heart of the dispatcher to runtime objects - // of the sequence of data types which define the probability model - using container_type = typename create_rtc>::type; - - using code_type = typename container_type::wrapped_type::code_type; - - /// get the number of models in the definition - static int getNumberOfModels() { return boost::mpl::size::value; } - - /// return highest stage of runtime container - container_type& operator*() { return mContainer; } - - /// functor to add weight to probability model at runtime container level - template - class addWeightFctr - { - public: - addWeightFctr(ValueType _v, WeightType _w) : value(_v), weight(_w) {} - ~addWeightFctr() {} - - using return_type = bool; - - template - return_type operator()(T& stage) - { - // the addWeight function belongs to the probability model as base - // of the specific model; funcions of the base can be accessed by - // static casting. This avoids an extra level of function calls. - return static_cast(*stage).addWeight(value, weight); - } - - private: - ValueType value; - WeightType weight; - }; - - /** - * add weight to current model - * - * Dispatcher increments to the next model definition after decoding if - * parameter switchToNextModel is true. - */ - template - bool addWeight(ValueType v, WeightType w, bool switchToNextModel = true) - { - bool result = mContainer.apply(mPosition, addWeightFctr(v, w)); - if (switchToNextModel && ++mPosition >= getNumberOfModels()) { - mPosition = 0; - } - return result; - } - - /** - * init model - */ - class initFctr - { - public: - initFctr(container_type& container) : mContainer(container) {} - ~initFctr() {} - - using return_type = int; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - return (*stage).init(); - } - - private: - container_type& mContainer; - }; - - /** - * init dispatcher and models - */ - int init() - { - mPosition = 0; - boost::mpl::for_each>(initFctr(mContainer)); - return 0; - } - - /** - * TODO: this is tailored to HuffmanCodec for the moment, some generic interface - * has to come - */ - class generateFctr - { - public: - generateFctr(container_type& container) : mContainer(container) {} - ~generateFctr() {} - - using return_type = int; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - return (*stage).GenerateHuffmanTree(); - } - - private: - container_type& mContainer; - }; - - /** - * TODO: maybe 'generate' is not the appropriate name - */ - int generate() - { - boost::mpl::for_each>(generateFctr(mContainer)); - return 0; - } - - /// functor to execute encoding on runtime container level - template - class encodeFctr - { - public: - encodeFctr(ValueType _v, CodeType& _code, uint16_t& _codeLength) : code(_code), value(_v), codeLength(_codeLength) - { - } - ~encodeFctr() {} - - using return_type = bool; - - template - return_type operator()(T& stage) - { - code = (*stage).Encode(value, codeLength); - return true; - } - - private: - CodeType& code; - ValueType value; - uint16_t& codeLength; - }; - - /** - * Encode a value - * - * Dispatcher increments to the next model definition after decoding if - * parameter switchToNextModel is true. - */ - template - bool encode(ValueType v, CodeType& code, uint16_t& codeLength, bool switchToNextModel = true) - { - bool result = mContainer.apply(mPosition, encodeFctr(v, code, codeLength)); - if (switchToNextModel && ++mPosition >= getNumberOfModels()) { - mPosition = 0; - } - return result; - } - - /// Functor to execute decoding on runtime container level - template - class decodeFctr - { - public: - decodeFctr(ValueType& _v, CodeType _code, uint16_t& _codeLength) : code(_code), value(_v), codeLength(_codeLength) - { - } - ~decodeFctr() {} - - using return_type = bool; - - template - return_type operator()(T& stage) - { - value = (*stage).Decode(code, codeLength); - return true; - } - - private: - CodeType code; - ValueType& value; - uint16_t& codeLength; - }; - - /** - * Decode a code sequence - * Code direction can be either from MSB to LSB or LSB to MSB, controlled - * by template parameter orderMSB of the probability model. - * - * Dispatcher increments to the next model definition after decoding if - * parameter switchToNextModel is true. - */ - template - bool decode(ValueType& v, CodeType code, uint16_t& codeLength, bool switchToNextModel = true) - { - bool result = mContainer.apply(mPosition, decodeFctr(v, code, codeLength)); - if (switchToNextModel && ++mPosition >= getNumberOfModels()) { - mPosition = 0; - } - return result; - } - - class getCodingDirectionFctr - { - public: - using return_type = bool; - template - return_type operator()(T& stage) - { - return T::wrapped_type::orderMSB; - } - }; - - /** - * Get coding direction for model at current position - */ - bool getCodingDirection() { return mContainer.apply(mPosition, getCodingDirectionFctr()); } - - /// write functor - class writeFctr - { - public: - writeFctr(std::ostream& out, container_type& container) : mOut(out), mContainer(container) {} - ~writeFctr() {} - - using return_type = std::ostream&; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - if (T::level::value > 0) { - mOut << std::endl; // blank line between dumps - } - mOut << T::level::value << " " << (*stage).getName() << std::endl; - (*stage).write(mOut); - return mOut; - } - - private: - std::ostream& mOut; - container_type& mContainer; - }; - - /** - * Write configuration - * - * TODO: introduce a general storage policy, a text file is used for now - */ - int write(const char* filename = nullptr) - { - std::ofstream ofile(filename); - boost::mpl::for_each>( - writeFctr(ofile.good() ? ofile : std::cout, mContainer)); - ofile.close(); - return 0; - } - - /// read functor - class readFctr - { - public: - readFctr(std::istream& in, container_type& container) : mIn(in), mContainer(container) {} - ~readFctr() {} - - using return_type = bool; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - std::string level, name, remaining; - mIn >> level; - mIn >> name; - if (!mIn) { - return false; - } - if (std::stoi(level) != T::level::value || name.compare((*stage).getName())) { - std::cerr << "Format error: expecting level '" << T::level::value << "' and name '" << (*stage).getName() - << "', got: " << level << " " << name << std::endl; - } - std::cout << "reading configuration for model " << name << std::endl; - std::getline(mIn, remaining); // flush the current line - (*stage).read(mIn); - return true; - } - - private: - std::istream& mIn; - container_type& mContainer; - }; - - /** - * Read configuration - * - * TODO: introduce a general storage policy, a text file is used for now - */ - int read(const char* filename) - { - std::ifstream input(filename); - if (!input.good()) { - return -1; - } - // TODO: probably need mpl fold here to propagate the return value - boost::mpl::for_each>(readFctr(input, mContainer)); - return 0; - } - - private: - /// position for cyclic dispatch - int mPosition; - /// the runtime container - container_type mContainer; -}; - -} // namespace data_compression -} // namespace o2 - -#endif diff --git a/Utilities/DataCompression/include/DataCompression/runtime_container.h b/Utilities/DataCompression/include/DataCompression/runtime_container.h deleted file mode 100644 index 363f4220e73f6..0000000000000 --- a/Utilities/DataCompression/include/DataCompression/runtime_container.h +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does 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 RUNTIME_CONTAINER_H -#define RUNTIME_CONTAINER_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 Author(s): 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 runtime_container.h -/// @author Matthias Richter -/// @since 2016-09-11 -/// @brief A general runtime container for a compile time sequence -/// This file is part of https://github.com/matthiasrichter/gNeric - -// clang-format off - -// A general runtime container for a compile time sequence -// of types. A mixin class is used to represent a member of each data -// type. Every data type in the sequence describes a mixin on top of -// the previous one. The runtime container accumulates the type -// properties. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace boost::mpl::placeholders; - -namespace gNeric { - -/** - * @class DefaultInterface - * @brief The default interface for the RuntimeContainer - * - * The common interface for the mixin class. In order to allow entry - * points to the different levels of the mixin, none of the interface - * functions has to be declared virtual. The function implementation of - * the top most mixin would be called otherwise. - * - * The mixin technique requires a base class, but it mostly makes sense in - * the picture of runtime polymorphism and virtual interfaces. The runtime - * container application is purely using static polymorphism which makes the - * base interface just to a technical aspect. - */ -class DefaultInterface -{ -public: - DefaultInterface() {} - ~DefaultInterface() {} - - void print() const {} -}; - -/** - * @brief Default initializer does nothing - */ -struct default_initializer -{ - template - void operator()(T&) {} -}; - -/** - * @brief An initializer for simple types - * The initializer makes use of truncation for non-float types, and - * over- and underflow to produce different values in the member - * of the individual stages in the container. - * - float types keep the fraction - * - integral types truncate the fraction - * - unsigned types undergo an underflow and produce big numbers - * - 8 bit char produces the '*' character - * - * Mainly for testing and illustration purposes. - */ -struct funny_initializer -{ - template - void operator()(T& v) {v=0; v-=214.5;} -}; - -/** - * @brief Default printer prints nothing - */ -struct default_printer -{ - template - bool operator()(const T& v, int level = -1) {return false;} -}; - -/** - * @brief Verbose printer prints level and content - */ -template -struct verbose_printer_base -{ - template - bool operator()(const T& v, int level = -1) { - std::cout << "RC mixin level " - << std::setw(2) - << level << ": " << v << std::endl; - return recursive; - } -}; - -/** - * @brief Verbose printer to print levels recursively - */ -struct recursive_printer : verbose_printer_base {}; - -// preserve backward compatibility -typedef recursive_printer verbose_printer; - -/** - * @brief Verbose printer to print a single level - */ -struct single_printer : verbose_printer_base {}; - -/** - * @brief Setter functor, forwards to the container mixin's set function - */ -template -class set_value { -public: - typedef void return_type; - typedef U value_type; - - set_value(U u) : mValue(u) {} - template - return_type operator()(T& t) { - *t = mValue; - } - -private: - set_value(); // forbidden - U mValue; -}; - -/** - * @brief Adder functor - */ -template -class add_value { -public: - typedef void return_type; - typedef U value_type; - - add_value(U u) : mValue(u) {} - template - return_type operator()(T& t) { - *t += mValue; - } - -private: - add_value(); // forbidden - U mValue; -}; - -/** - * @brief Getter functor, forwards to the container mixin's get function - * - * TODO: make a type trait to either return t.get() if its a container - * instance or t directly if it is the member object - */ -template -class get_value { -public: - typedef U return_type; - typedef U value_type; - class NullType {}; -private: - /* could not solve the problem that one has to instantiate Traits - with a fixed number of template arguments where wrapped_type - would need to be provided already to go into the specialization - template - struct Traits { - typedef NullType container_type; - typedef InstanceType type; - static return_type apply(InstanceType& c) { - std::cout << "Traits"; - return c; - } - }; - // specialization for container instances - template - struct Traits { - typedef InstanceType container_type; - typedef typename InstanceType::wrapped_type type; - static return_type apply(InstanceType& c) { - std::cout << "specialized Traits"; - return c.get(); - } - }; - */ - -public: - template - return_type operator()(T& t) { - return t.get(); - //return (typename Traits::type)(t); - } -}; - - -/****************************************************************************** - * @brief apply functor to the wrapped member object in the runtime container - * This meta function recurses through the list while incrementing the index - * and calls the functor at the required position - * - * @note internal meta function for the RuntimeContainers' apply function - */ -template < - typename _ContainerT // container type - , typename _IndexT // data type of position index - , typename _Iterator // current iterator position - , typename _End // end iterator position - , _IndexT _Index // current index - , typename F // functor - > -struct rc_apply_at -{ - static typename F::return_type apply( _ContainerT& c, _IndexT position, F& f ) - { - if ( position == _Index ) { - // this is the queried position, make the type cast to the current - // stage of the runtime container and execute function for it. - // Terminate loop by forwarding _End as _Iterator and thus - // calling the specialization - typedef typename boost::mpl::deref< _Iterator >::type stagetype; - stagetype& stage = static_cast(c); - return f(stage); - } else { - // go to next element - return rc_apply_at< - _ContainerT - , _IndexT - , typename boost::mpl::next< _Iterator >::type - , _End - , _Index + 1 - , F - >::apply( c, position, f ); - } - } -}; -// specialization: end of recursive loop, kicks in if _Iterator matches -// _End. -// here we end up if the position parameter is out of bounds -template < - typename _ContainerT // container type - , typename _IndexT // data type of position index - , typename _End // end iterator position - , _IndexT _Index // current index - , typename F // functor - > -struct rc_apply_at<_ContainerT - , _IndexT - , _End - , _End - , _Index - , F - > -{ - static typename F::return_type apply( _ContainerT& c, _IndexT position, F& f ) - { - // TODO: this is probably the place to throw an exeption because - // we are out of bound - return typename F::return_type(0); - } -}; - -/** - * Apply functor to the specified container level - * - * Ignores parameter '_IndexT' - */ -template -struct rc_apply { - typedef typename _ContainerT::types types; - static typename F::return_type apply(_ContainerT& c, _IndexT /*ignored*/, F& f) - { - return f(static_cast<_StageT&>(c)); - } -}; - -/** - * Generalized dispatcher with the ability for code unrolling - * - * The optional template parameter 'Position' can be used to cast directly to - * the specified level in the runtime container and apply the functor without - * the recursive loop. The template call with default parameters forwards to - * the recursive call because 'Position' is set to out of list range. - */ -template - , typename _IndexT = int - > -struct rc_dispatcher { - typedef typename _ContainerT::types types; - typedef typename boost::mpl::if_< - boost::mpl::less > - , rc_apply<_ContainerT, typename boost::mpl::at::type, _IndexT, F> - , rc_apply_at< - _ContainerT - , _IndexT - , typename boost::mpl::begin::type - , typename boost::mpl::end::type - , 0 - , F - > - >::type type; - - static typename F::return_type apply(_ContainerT& c, _IndexT position, F& f) { - return type::apply(c, position, f); - } -}; - -/** - * @class RuntimeContainer The base for the mixin class - * @brief the technical base of the mixin class - * - * The class is necessary to provide the innermost functionality of the - * mixin. - * - * The level of the mixin is encoded in the type 'level' which is - * incremented in each mixin stage. - */ -template -struct RuntimeContainer : public InterfacePolicy -{ - InitializerPolicy _initializer; - PrinterPolicy _printer; - typedef boost::mpl::int_<-1> level; - typedef boost::mpl::vector<>::type types; - - /// get size which is 0 at this level - constexpr std::size_t size() const {return 0;} - - void print() { - const char* string = "base"; - _printer(string, level::value); - } - - // not yet clear if we need the setter and getter in the base class - // at least wrapped_type is not defined in the base - //void set(wrapped_type) {mMember = v;} - //wrapped_type get() const {return mMember;} - -}; - -/** - * @class rc_mixin Components for the mixin class - * @brief Mixin component is used with different data types - * - * Each mixin component has a member of the specified type. The container - * level exports the following data types to the outside: - * - wrapped_type the data type at this level - * - mixin_type composed type at this level - * - types mpl sequence containing all level types - * - level a data type containing the level - */ -template -class rc_mixin : public BASE -{ -public: - rc_mixin() : mMember() {BASE::_initializer(mMember);} - - /// each stage of the mixin class wraps one type - typedef T wrapped_type; - /// this is the self type - typedef rc_mixin mixin_type; - /// a vector of all mixin stage types so far - typedef typename boost::mpl::push_back::type types; - /// increment the level counter - typedef typename boost::mpl::plus< typename BASE::level, boost::mpl::int_<1> >::type level; - void print() { - // use the printer policy of this level, the policy returns - // a bool determining whether to call the underlying level - if (BASE::_printer(mMember, level::value)) { - BASE::print(); - } - } - - /// get size at this stage - constexpr std::size_t size() const {return level::value + 1;} - /// set member wrapped object - void set(wrapped_type v) {mMember = v;} - /// get wrapped object - wrapped_type get() const {return mMember;} - /// get wrapped object reference - wrapped_type& operator*() {return mMember;} - /// assignment operator to wrapped type - wrapped_type& operator=(const wrapped_type& v) {mMember = v; return mMember;} - /// type conversion to wrapped type - operator wrapped_type() const {return mMember;} - /// operator - wrapped_type& operator+=(const wrapped_type& v) {mMember += v; return mMember;} - /// operator - wrapped_type operator+(const wrapped_type& v) {return mMember + v;} - - /// a functor wrapper dereferencing the RC container instance - /// the idea is to use this extra wrapper to apply the functor directly to - /// the wrapped type, see the comment below - template - class member_apply_at { - public: - member_apply_at(F& f) : mFunctor(f) {} - typedef typename F::return_type return_type; - template - typename F::return_type operator()(_T& me) { - return mFunctor(*me); - } - private: - member_apply_at(); //forbidden - F& mFunctor; - }; - - /// apply functor to the runtime object at index - /// TODO: there is a performance issue with this solution, introducing another - /// level of functors makes the access much slower compared with applying to - /// container instance and using container member functions, tested with the - /// add_value functor and bench_runtime_container, also the actual operation - /// needs to be checked, the result is not correct for the last check of - /// 100000000 iterations - /* - template - typename F::return_type applyToMember(int index, F f) { - return apply(index, member_apply_at(f)); - } - */ - - /* - * Apply a functor to the runtime container at index - * - * For performance tests there is a template option to do an explicite loop - * unrolling for the first n (=10) elements. This is however only effective - * if the compiler optimization is switched of. This is in the end a nice - * demonstrator for the potential of compiler optimization. Unrolling is - * switched on with the compile time switch RC_UNROLL. - */ - template - typename F::return_type apply(int index, F f) { - if (unroll) {// this is a compile time switch - // do unrolling for the first n elements and forward to generic - // recursive function for the rest. - switch (index) { - case 0: return rc_dispatcher, int>::apply(*this, 0, f); - case 1: return rc_dispatcher, int>::apply(*this, 1, f); - case 2: return rc_dispatcher, int>::apply(*this, 2, f); - case 3: return rc_dispatcher, int>::apply(*this, 3, f); - case 4: return rc_dispatcher, int>::apply(*this, 4, f); - case 5: return rc_dispatcher, int>::apply(*this, 5, f); - case 6: return rc_dispatcher, int>::apply(*this, 6, f); - case 7: return rc_dispatcher, int>::apply(*this, 7, f); - case 8: return rc_dispatcher, int>::apply(*this, 8, f); - case 9: return rc_dispatcher, int>::apply(*this, 9, f); - } - } - return rc_dispatcher::apply(*this, index, f); - } - -private: - T mMember; -}; - -/** - * @brief Applying rc_mixin with the template parameters as placeholders - * The wrapping into an mpl lambda is necessary to separate placeholder scopes - * in the mpl fold operation. - */ -typedef typename boost::mpl::lambda< rc_mixin<_1, _2> >::type apply_rc_mixin; - -/** - * @brief check the mixin level to be below specified level - * - * @note: the number is specified as a type, e.g. boost::mpl:int_<3> - */ -template< typename T, typename N > struct rtc_less -: boost::mpl::bool_<(T::level::value < boost::mpl::minus>::value) > {}; - -template< typename T, typename N > struct rtc_equal -: boost::mpl::bool_::type> {}; - -/** - * @brief create the runtime container type - * The runtime container type is build from a list of data types, the recursive - * build can be optionally stopped at the level of argument N. - * - * Usage: typedef create_rtc::type container_type; - */ -template> -struct create_rtc -{ - typedef typename boost::mpl::lambda< - // mpl fold loops over all elements in the list of the first template - // parameter and provides this as placeholder _2; for every element the - // operation of the third template parameter is applied to the result of - // the previous stage which is provided as placeholder _1 to the operation - // and initialized to the second template argument for the very first - // operation - typename boost::mpl::fold< - // list of types, each element provided as placeholder _1 - Types - // initializer for the _1 placeholder - , Base - // recursively applied operation, depending on the outcome of rtc_less - // either the next mixin level is applied or the current state is used - , boost::mpl::if_< - rtc_less<_1, N > - // apply mixin level - , boost::mpl::apply2< boost::mpl::protect::type, _1, _2 > - // keep current state by identity - , boost::mpl::identity<_1> - > - >::type - >::type type; -}; - -/** - * @brief create an mpl vector of mixin types - * Every stage in the runtime container contains all the previous ones. - * The resulting mpl vector of this meta function contains all individual - * stages. - * - * Usage: typedef create_rtc_types::type container_types; - */ -template> -struct create_rtc_types -{ - typedef typename boost::mpl::fold< - boost::mpl::range_c - , boost::mpl::vector< > - , boost::mpl::push_back<_1, create_rtc>>> - >::type type; -}; - -};// namespace gNeric -// clang-format on - -#endif diff --git a/Utilities/DataCompression/tpccluster_parameter_model.h b/Utilities/DataCompression/tpccluster_parameter_model.h deleted file mode 100644 index e8455399f17c1..0000000000000 --- a/Utilities/DataCompression/tpccluster_parameter_model.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. - -#include "DataCompression/dc_primitives.h" -#include "DataCompression/HuffmanCodec.h" -#include -#include -#include - -/** - * Parameter model definitions - * - boost mpl vector of alphabets - */ -using tpccluster_parameter = - boost::mpl::vector>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>>; -/** - * Definition of Huffman probability models for the above defined alphabets - * - * This is a temporary definition, the mpl sequence can be created automatically - * from the list of alphabet types, but did not manage so far (see below) - */ -template -using Model = o2::HuffmanModel>, - o2::HuffmanNode>, true>; - -using tpccluster_parameter_models = - boost::mpl::vector>, - Model>, - Model>, - Model>, - Model>, - Model>, - Model>>; - -/** new approach - using basemodels = foldtype - < tpccluster_parameter, - mpl::lambda>::type - >::type; - - using tpcmodels = foldtype - < basemodels, - mpl::lambda<_, o2::HuffmanNode>>::type - >::type; -*/ - -/** - * this was an attemp to create the vector of Huffman models directly - * from the vector of alphabets - * - * For the moment, the placeholders of mpl fold are not expanded, so there are - * unknown types in the end - */ -/// very first attemp -//using namespace boost::mpl::placeholders; -// -//typedef boost::mpl::fold< -// tpccluster_parameter, -// boost::mpl::vector<>, -// boost::mpl::push_back< -// _1 -// , AliceO2::HuffmanModel< ProbabilityModel< _2 >, AliceO2::HuffmanNode>, true> -// > -// >::type models_t; - -/// trying with additional lambda levels -//typedef boost::mpl::string<'T','e','s','t'>::type TestAlphabetName; -//typedef ContiguousAlphabet TestAlphabet; -// -//typedef typename boost::mpl::lambda< ProbabilityModel< _1 > > apply_alphabet; -//typedef boost::mpl::apply1::type TestAlphabetModel; -//typedef typename boost::mpl::lambda< AliceO2::HuffmanModel< _1, AliceO2::HuffmanNode>, true> > apply_probabilitymodel; -//typedef typename boost::mpl::apply1::type, TestAlphabetModel>::type TestHuffmanModel; -// -//TestAlphabetModel object; -//typedef TestAlphabetModel::value_type vtype; -// -//std::cout << object.getName() << std::endl; - -//typedef boost::mpl::fold< -// tpccluster_parameter, -// boost::mpl::vector<>, -// boost::mpl::push_back< -// _1 -// , boost::mpl::apply1< boost::mpl::protect::type, _2 > -// > -// >::type models_t; diff --git a/Utilities/Tools/jobutils.sh b/Utilities/Tools/jobutils.sh index 54f1394197815..7853f281f004b 100644 --- a/Utilities/Tools/jobutils.sh +++ b/Utilities/Tools/jobutils.sh @@ -107,6 +107,7 @@ taskwrapper() { STARTTIME=$SECONDS + rm -f encountered_exceptions_list_${logfile} # launch the actual command in the background echo "Launching task: ${command} &> $logfile &" # the command might be a complex block: For the timing measurement below @@ -191,10 +192,10 @@ taskwrapper() { exclude_pattern="-e \"To change the tolerance or the exception severity\"" - grepcommand="grep -a -H ${pattern} $logfile ${JOBUTILS_JOB_SUPERVISEDFILES} | grep -a -v ${exclude_pattern} >> encountered_exceptions_list 2>/dev/null" + grepcommand="grep -a -H ${pattern} $logfile ${JOBUTILS_JOB_SUPERVISEDFILES} | grep -a -v ${exclude_pattern} >> encountered_exceptions_list_${logfile} 2>/dev/null" eval ${grepcommand} - grepcommand="cat encountered_exceptions_list 2>/dev/null | wc -l" + grepcommand="cat encountered_exceptions_list_${logfile} 2>/dev/null | wc -l" # using eval here since otherwise the pattern is translated to a # a weirdly quoted stringlist RC=$(eval ${grepcommand}) diff --git a/Utilities/Tools/jobutils2.sh b/Utilities/Tools/jobutils2.sh index ba96b97da090b..b7c8466f54313 100644 --- a/Utilities/Tools/jobutils2.sh +++ b/Utilities/Tools/jobutils2.sh @@ -395,10 +395,13 @@ getNumberOfPhysicalCPUCores() { fi else # Do something under GNU/Linux platform - CORESPERSOCKET=`lscpu | grep "Core(s) per socket" | awk '{print $4}'` - SOCKETS=`lscpu | grep "Socket(s)" | awk '{print $2}'` + # + # Gets the cores per socket by counting unique cores on socket 0. + # Gets sockets by counting unique socket ids. The grepping is done in any case to avoid matching comments. + CORESPERSOCKET=$(lscpu -p=cpu,socket | grep "^[0-9]\+,0" | sort | uniq | wc -l) + SOCKETS=$(lscpu -p=socket | grep -e "^[0-9]" | sort | uniq | wc -l) fi - N=`bc <<< "${CORESPERSOCKET}*${SOCKETS}"` + N=$((${CORESPERSOCKET}*${SOCKETS})) echo "${N}" } diff --git a/Utilities/rANS/include/rANS/internal/common/utils.h b/Utilities/rANS/include/rANS/internal/common/utils.h index 34f2a5c6d6b30..8b8cfd9c15154 100644 --- a/Utilities/rANS/include/rANS/internal/common/utils.h +++ b/Utilities/rANS/include/rANS/internal/common/utils.h @@ -152,11 +152,11 @@ inline uint32_t safeadd(uint32_t a, uint32_t b) } // namespace internal -inline constexpr std::uint8_t operator"" _u8(unsigned long long int value) { return static_cast(value); }; -inline constexpr std::int8_t operator"" _i8(unsigned long long int value) { return static_cast(value); }; +inline constexpr std::uint8_t operator""_u8(unsigned long long int value) { return static_cast(value); }; +inline constexpr std::int8_t operator""_i8(unsigned long long int value) { return static_cast(value); }; -inline constexpr std::uint16_t operator"" _u16(unsigned long long int value) { return static_cast(value); }; -inline constexpr std::int16_t operator"" _i16(unsigned long long int value) { return static_cast(value); }; +inline constexpr std::uint16_t operator""_u16(unsigned long long int value) { return static_cast(value); }; +inline constexpr std::int16_t operator""_i16(unsigned long long int value) { return static_cast(value); }; namespace utils { diff --git a/Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h b/Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h deleted file mode 100644 index 2c703ede64493..0000000000000 --- a/Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-2023 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file HistogramInterface.h -/// @author Michael Lettrich -/// @brief Operations that will be performed on a histogram - -#ifndef RANS_INTERNAL_CONTAINERS_HISTOGRAMINTERFACE_H_ -#define RANS_INTERNAL_CONTAINERS_HISTOGRAMINTERFACE_H_ - -#include - -#include "rANS/internal/common/utils.h" - -namespace o2::rans::internal -{ - -template -class HistogramInterface -{ - - public: - using source_type = source_T; - using value_type = value_T; - using difference_type = difference_T; - - // operations - template - inline derived_T& addSamples(source_IT begin, source_IT end) - { - static_assert(utils::isCompatibleIter_v); - - if (begin == end) { - return static_cast(*this); - } else { - return static_cast(this)->addSamples(begin, end); - } - }; - - inline derived_T& addSamples(gsl::span samples) - { - return addSamples(samples.data(), samples.data() + samples.size()); - }; - - template - inline derived_T& addFrequencies(freq_IT begin, freq_IT end, difference_type offset) - { - static_assert(utils::isCompatibleIter_v); - - if (begin == end) { - return static_cast(*this); - } else { - return static_cast(this)->addFrequencies(begin, end, offset); - } - }; - - inline derived_T& addFrequencies(gsl::span frequencies, difference_type offset) - { - return addFrequencies(frequencies.data(), frequencies.data() + frequencies.size(), offset); - }; - - derived_T& operator+(derived_T& other) - { - return addFrequencies(other.cbegin(), other.cbegin(), other.getOffset()); - }; - - protected: - HistogramInterface() = default; - - template - HistogramInterface(freq_IT begin, freq_IT end, difference_type offset) - { - static_assert(utils::isIntegralIter_v); - addFrequencies(begin, end, offset); - }; -}; - -} // namespace o2::rans::internal - -#endif /* RANS_INTERNAL_CONTAINERS_HISTOGRAMINTERFACE_H_ */ diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 51812fc84ef1a..3e8f012fea4b5 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,12 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 3 +# FindO2GPU.cmake Version 13 + +set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) +set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) +set(CUDA_COMPUTETARGET_DEFAULT_MINIMAL 75-virtual) +set(HIP_AMDGPUTARGET_DEFAULT_MINIMAL gfx906) if(NOT DEFINED ENABLE_CUDA) set(ENABLE_CUDA "AUTO") @@ -32,45 +37,91 @@ if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") endif() if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "default") - set(CUDA_COMPUTETARGET 86 89) + set(CUDA_COMPUTETARGET ${CUDA_COMPUTETARGET_DEFAULT_FULL}) endif() if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") - set(HIP_AMDGPUTARGET gfx906;gfx908) + set(HIP_AMDGPUTARGET ${HIP_AMDGPUTARGET_DEFAULT_FULL}) endif() -function(set_target_cuda_arch target) - if(CUDA_COMPUTETARGET AND (CUDA_COMPUTETARGET MATCHES "86" OR CUDA_COMPUTETARGET MATCHES "89")) - message(STATUS "Using optimized CUDA settings for Ampere GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) - elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "75") - message(STATUS "Using optimized CUDA settings for Turing GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_TURING) +function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend + + string(REGEX MATCH "^[ \t\r\n]*[0-9]+" CUDA_FIRST_TARGET "${CUDA_COMPUTETARGET}") + string(STRIP "${CUDA_FIRST_TARGET}" CUDA_FIRST_TARGET) + if(NOT CUDA_FIRST_TARGET) + set(CUDA_FIRST_TARGET 86) + message(STATUS "CUDA_COMPUTETARGET not set, defaulting CUDA optimization for architecture ${CUDA_FIRST_TARGET}") + endif() + if(CUDA_FIRST_TARGET GREATER_EQUAL 86) + set(CUDA_TARGET AMPERE) + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 75) + set(CUDA_TARGET TURING) + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 60) + set(CUDA_TARGET PASCAL) + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 30) + set(CUDA_TARGET KEPLER) + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 20) + set(CUDA_TARGET FERMI) else() - message(STATUS "Defaulting optimized CUDA settings for Ampere GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) + set(CUDA_TARGET TESLA) endif() -endfunction() -function(set_target_hip_arch target) - if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") - message(STATUS "Using optimized HIP settings for MI50 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) - elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx908") - message(STATUS "Using optimized HIP settings for MI100 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI2xx) - elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") - message(STATUS "Using optimized HIP settings for MI210 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI2xx) + string(REGEX MATCH "^[ \t\r\n]*gfx[0-9]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") + string(STRIP "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) + string(REGEX REPLACE "^gfx" "" HIP_FIRST_TARGET "${HIP_FIRST_TARGET}") + if(NOT HIP_FIRST_TARGET) + set(HIP_FIRST_TARGET 906) + message(STATUS "HIP_AMDGPUTARGET not set, defaulting HIP optimization for architecture ${HIP_FIRST_TARGET}") + endif() + string(TOLOWER "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) + string(REGEX MATCH "....$" HIP_FIRST_TARGET_PADDED "0000${HIP_FIRST_TARGET}") + if(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "1000") + set(HIP_TARGET RDNA) + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "090a") + set(HIP_TARGET MI210) + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "0908") + set(HIP_TARGET MI100) + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "0906") + set(HIP_TARGET VEGA) else() - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) + set(HIP_TARGET VEGA) endif() + + if(backend STREQUAL "CUDA") # CUDA filter + message(STATUS "Using optimized CUDA settings for ${CUDA_TARGET} GPU (sm_${CUDA_FIRST_TARGET})") + set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) + elseif(backend STREQUAL "HIP") # HIP filter + set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) + message(STATUS "Using optimized HIP settings for ${HIP_TARGET} GPU (gfx${HIP_FIRST_TARGET})") + elseif(backend STREQUAL "ALL" OR backend STREQUAL "AUTO") # Return all / enabled backends + set(TARGET_ARCH) + if(CUDA_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "${CUDA_TARGET}") + endif() + if(HIP_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "${HIP_TARGET}") + endif() + if(OPENCL_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "OPENCL") + endif() + set(TARGET_ARCH "${TARGET_ARCH}" PARENT_SCOPE) + else() + message(FATAL_ERROR "Unknown backend provided: ${backend}") + endif() +endfunction() + +function(set_target_gpu_arch backend target) + detect_gpu_arch("${backend}") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_${TARGET_ARCH}) endfunction() # Need to strip c++17 imposed by alidist defaults STRING(REGEX REPLACE "\-std=[^ ]*" "" O2_GPU_CMAKE_CXX_FLAGS_NOSTD "${CMAKE_CXX_FLAGS}") # ================================== Fast Math / Deterministic Mode ================================== +if(DEFINED ENV{O2_OVERRIDE_GPUCA_DETERMINISTIC_MODE}) + set(GPUCA_DETERMINISTIC_MODE $ENV{O2_OVERRIDE_GPUCA_DETERMINISTIC_MODE}) +endif() # set(GPUCA_DETERMINISTIC_MODE WHOLEO2) # Override set(GPUCA_DETERMINISTIC_MODE_MAP_OFF 0) set(GPUCA_DETERMINISTIC_MODE_MAP_NO_FAST_MATH 1) # No -ffast-math and similar compile flags for GPU folder @@ -109,7 +160,7 @@ if(ENABLE_CUDA) if(CUDA_COMPUTETARGET) set(CMAKE_CUDA_ARCHITECTURES ${CUDA_COMPUTETARGET}) else() - set(CMAKE_CUDA_ARCHITECTURES 75-virtual) + set(O2_GPU_CUDA_UPDATE_NATIVE_ARCHITECTURE 1) endif() set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) set(CMAKE_CUDA_STANDARD_REQUIRED TRUE) @@ -139,9 +190,9 @@ if(ENABLE_CUDA) message(${FAILURE_SEVERITY} "CUDA was found but cannot be enabled") set(CMAKE_CUDA_COMPILER OFF) endif() - find_path(THRUST_INCLUDE_DIR thrust/version.h PATHS ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} NO_DEFAULT_PATH) + find_path(THRUST_INCLUDE_DIR thrust/version.h PATHS ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} PATH_SUFFIXES "" cccl NO_DEFAULT_PATH) if(THRUST_INCLUDE_DIR STREQUAL "THRUST_INCLUDE_DIR-NOTFOUND") - message(${FAILURE_SEVERITY} "CUDA found but thrust not available") + message(${FAILURE_SEVERITY} "CUDA found but thrust not available, looked under: ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}") set(CMAKE_CUDA_COMPILER OFF) endif() if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.8") @@ -149,6 +200,13 @@ if(ENABLE_CUDA) set(CMAKE_CUDA_COMPILER OFF) endif() endif() + if(NOT CMAKE_CUDA_ARCHITECTURES OR O2_GPU_CUDA_UPDATE_NATIVE_ARCHITECTURE) + if(NOT CMAKE_CUDA_ARCHITECTURES_NATIVE STREQUAL "" AND NOT CMAKE_CUDA_ARCHITECTURES_NATIVE MATCHES "No CUDA devices found") + set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES_NATIVE}) + else() + set(CMAKE_CUDA_ARCHITECTURES ${CUDA_COMPUTETARGET_DEFAULT_MINIMAL}) + endif() + endif() if(CMAKE_CUDA_COMPILER) set(CMAKE_CUDA_FLAGS "-Xcompiler \"${O2_GPU_CMAKE_CXX_FLAGS_NOSTD}\" ${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr --extended-lambda -Xcompiler -Wno-attributes -Wno-deprecated-gpu-targets ${GPUCA_CUDA_DENORMALS_FLAGS}") set(CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER} "-Xcompiler \"${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}\" ${CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") @@ -159,14 +217,14 @@ if(ENABLE_CUDA) if (NOT ENABLE_CUDA STREQUAL "AUTO") string(APPEND CMAKE_CUDA_FLAGS " --allow-unsupported-compiler") endif() - if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") + if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG" AND NOT GPUCA_BUILD_DEBUG_HOSTONLY) string(APPEND CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " -lineinfo -Xptxas -O0") else() string(APPEND CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " -Xptxas -O4 -Xcompiler -O4") endif() if(GPUCA_DETERMINISTIC_MODE GREATER_EQUAL ${GPUCA_DETERMINISTIC_MODE_MAP_NO_FAST_MATH}) string(APPEND CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " ${GPUCA_CUDA_NO_FAST_MATH_FLAGS}") - elseif(NOT CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") + elseif(NOT CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG" OR GPUCA_BUILD_DEBUG_HOSTONLY) string(APPEND CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " -use_fast_math ${GPUCA_CUDA_DENORMALS_FLAGS}") endif() if(CMAKE_CXX_FLAGS MATCHES "(^| )-Werror( |$)") @@ -177,7 +235,7 @@ if(ENABLE_CUDA) endif() set(CUDA_ENABLED ON) - message(STATUS "CUDA found (Version ${CMAKE_CUDA_COMPILER_VERSION})") + message(STATUS "CUDA found (Version ${CMAKE_CUDA_COMPILER_VERSION}, Architectures ${CMAKE_CUDA_ARCHITECTURES})") elseif(NOT ENABLE_CUDA STREQUAL "AUTO") message(FATAL_ERROR "CUDA not found (Compiler: ${CMAKE_CUDA_COMPILER})") else() @@ -218,15 +276,14 @@ if(ENABLE_OPENCL) AND NOT LLVM_SPIRV STREQUAL "LLVM_SPIRV-NOTFOUND" AND OPENCL_COMPATIBLE_CLANG_FOUND) set(OPENCL_ENABLED_SPIRV ON) - message(STATUS "Using CLANG ${LLVM_CLANG} and ${LLVM_SPIRV} for SPIR-V compilation") endif () if(OPENCL_COMPATIBLE_CLANG_FOUND AND (OpenCL_VERSION_STRING VERSION_GREATER_EQUAL 2.2 OR OPENCL_ENABLED_SPIRV)) set(OPENCL_ENABLED ON) - message(STATUS "Found OpenCL 2 (${OpenCL_VERSION_STRING} SPIR-V ${OPENCL_ENABLED_SPIRV} with CLANG ${LLVM_PACKAGE_VERSION})") + message(STATUS "Found OpenCL ${OpenCL_VERSION_STRING} (SPIR-V ${OPENCL_ENABLED_SPIRV} ${LLVM_CLANG} ${LLVM_PACKAGE_VERSION} ${LLVM_SPIRV})") elseif(NOT ENABLE_OPENCL STREQUAL "AUTO") - message(FATAL_ERROR "OpenCL 2.x not available") + message(FATAL_ERROR "OpenCL >= 2.x not available") else() set(OPENCL_ENABLED OFF) endif() @@ -287,7 +344,6 @@ if(ENABLE_HIP) set(CMAKE_HIP_HOST_COMPILER "$ENV{GCC_TOOLCHAIN_ROOT}/bin/gcc") endif() enable_language(HIP) - message(STATUS "HIP language enabled: ${CMAKE_HIP_COMPILER}") endif() elseif(NOT ENABLE_HIP STREQUAL "AUTO") message(FATAL_ERROR "HIP requested, but CMAKE_PREFIX_PATH env variable does not contain rocm folder!") @@ -298,7 +354,6 @@ if(ENABLE_HIP) if(hip_FOUND AND hipcub_FOUND AND rocthrust_FOUND AND rocprim_FOUND AND hip_HIPCC_EXECUTABLE AND hip_HIPIFY_PERL_EXECUTABLE) set(HIP_ENABLED ON) set_target_properties(roc::rocthrust PROPERTIES IMPORTED_GLOBAL TRUE) - message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION})") set(CMAKE_HIP_FLAGS "${O2_GPU_CMAKE_CXX_FLAGS_NOSTD} ${CMAKE_HIP_FLAGS} ${GPUCA_HIP_DENORMALS_FLAGS}") set(CMAKE_HIP_FLAGS_${CMAKE_BUILD_TYPE_UPPER} "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} ${CMAKE_HIP_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") string(APPEND CMAKE_HIP_FLAGS " -fgpu-defer-diag -mllvm -amdgpu-enable-lower-module-lds=false -mllvm -amdgpu-function-calls=true -Wno-invalid-command-line-argument -Wno-unused-command-line-argument -Wno-invalid-constexpr -Wno-ignored-optimization-argument -Wno-unused-private-field -Wno-pass-failed ") @@ -307,13 +362,14 @@ if(ENABLE_HIP) endif() if(GPUCA_DETERMINISTIC_MODE GREATER_EQUAL ${GPUCA_DETERMINISTIC_MODE_MAP_NO_FAST_MATH}) string(APPEND CMAKE_HIP_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " ${GPUCA_CXX_NO_FAST_MATH_FLAGS}") - elseif(NOT CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") + elseif(NOT CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG" OR GPUCA_BUILD_DEBUG_HOSTONLY) string(APPEND CMAKE_HIP_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " -ffast-math -O3") endif() string(REGEX REPLACE "(gfx1[0-9]+;?)" "" CMAKE_HIP_ARCHITECTURES "${CMAKE_HIP_ARCHITECTURES}") # ROCm currently doesn’t support integrated graphics if(HIP_AMDGPUTARGET) set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}") endif() + message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, ${CMAKE_HIP_COMPILER}, Architectures ${CMAKE_HIP_ARCHITECTURES})") else() set(HIP_ENABLED OFF) endif() diff --git a/dependencies/FindOpenMPMacOS.cmake b/dependencies/FindOpenMPMacOS.cmake index 264ce5398a331..9bdeb35ecd46d 100644 --- a/dependencies/FindOpenMPMacOS.cmake +++ b/dependencies/FindOpenMPMacOS.cmake @@ -1,28 +1,53 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + find_library(OpenMP_LIBRARY - NAMES omp + NAMES omp libomp + HINTS + /opt/homebrew/opt/libomp/lib + /usr/local/opt/libomp/lib ) find_path(OpenMP_INCLUDE_DIR - omp.h + NAMES omp.h + HINTS + /opt/homebrew/opt/libomp/include + /usr/local/opt/libomp/include ) mark_as_advanced(OpenMP_LIBRARY OpenMP_INCLUDE_DIR) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OpenMP DEFAULT_MSG - OpenMP_LIBRARY OpenMP_INCLUDE_DIR) +find_package_handle_standard_args( + OpenMPMacOS + DEFAULT_MSG + OpenMP_LIBRARY OpenMP_INCLUDE_DIR +) -if (OpenMP_FOUND) +if (OpenMPMacOS_FOUND) set(OpenMP_LIBRARIES ${OpenMP_LIBRARY}) set(OpenMP_INCLUDE_DIRS ${OpenMP_INCLUDE_DIR}) - set(OpenMP_COMPILE_OPTIONS -Xpreprocessor -fopenmp) - set(OpenMP_CXX_FOUND True) - set(OpenMPMacOS_FOUND True) - add_library(OpenMP::OpenMP_CXX SHARED IMPORTED) + set(OpenMP_CXX_FOUND TRUE) + set(OpenMP_FOUND TRUE) + + add_library(OpenMP::OpenMP_CXX INTERFACE IMPORTED) set_target_properties(OpenMP::OpenMP_CXX PROPERTIES - IMPORTED_LOCATION ${OpenMP_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES "${OpenMP_INCLUDE_DIRS}" - INTERFACE_COMPILE_OPTIONS "${OpenMP_COMPILE_OPTIONS}" + INTERFACE_COMPILE_OPTIONS "-Xclang;-fopenmp" + INTERFACE_LINK_LIBRARIES "${OpenMP_LIBRARIES}" + ) + message(STATUS + "Found OpenMP (macOS workaround): " + "library=${OpenMP_LIBRARY}, " + "include=${OpenMP_INCLUDE_DIR}" ) endif() diff --git a/doc/data/2021-01-o2_prs.json b/doc/data/2021-01-o2_prs.json index e287eaeeda857..4ccf435f6086a 100644 --- a/doc/data/2021-01-o2_prs.json +++ b/doc/data/2021-01-o2_prs.json @@ -895,7 +895,7 @@ }, { "node": { - "path": "GPU/GPUTracking/Base/GPUDataTypes.h" + "path": "GPU/GPUTracking/Base/GPUDataTypesIO.h" } }, { @@ -1730,7 +1730,7 @@ }, { "node": { - "path": "GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx" + "path": "GPU/GPUTracking/Interface/GPUO2ConfigurableParam.cxx" } }, { @@ -2445,7 +2445,7 @@ }, { "node": { - "path": "GPU/GPUTracking/DataTypes/GPUDataTypes.h" + "path": "GPU/GPUTracking/DataTypes/GPUDataTypesIO.h" } }, { diff --git a/log.txt b/log.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/macro/CMakeLists.txt b/macro/CMakeLists.txt index 843ad4a3be0ab..0bb5650364b06 100644 --- a/macro/CMakeLists.txt +++ b/macro/CMakeLists.txt @@ -35,7 +35,6 @@ install(FILES CheckDigits_mft.C runTPCRefit.C run_CRUDataSkimming_its.C run_calib_tof.C - run_clus_itsSA.C run_clus_tof.C run_clus_tpc.C run_clus_emcal.C @@ -48,7 +47,6 @@ install(FILES CheckDigits_mft.C compareTOFClusters.C run_rawdecoding_its.C run_rawdecoding_mft.C - run_trac_its.C CreateBCPattern.C UploadDummyAlignment.C UploadMatBudLUT.C @@ -58,6 +56,7 @@ install(FILES CheckDigits_mft.C CreateGRPLHCIFObject.C getTimeStamp.C CreateSampleIRFrames.C + convert_onnx_to_root_serialized.C DESTINATION share/macro/) # FIXME: a lot of macros that are here should really be elsewhere. Those which @@ -149,6 +148,9 @@ o2_add_test_root_macro(checkTOFMatching.C O2::SimulationDataFormat O2::DataFormatsTOF) +o2_add_test_root_macro(convert_onnx_to_root_serialized.C + PUBLIC_LINK_LIBRARIES O2::GlobalTracking) + # FIXME: move to subsystem dir o2_add_test_root_macro(compareTopologyDistributions.C PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT @@ -240,14 +242,6 @@ o2_add_test_root_macro(run_calib_tof.C O2::DetectorsBase O2::GlobalTracking) -# FIXME: move to subsystem dir -o2_add_test_root_macro(run_clus_itsSA.C - PUBLIC_LINK_LIBRARIES O2::DetectorsBase - O2::ITSReconstruction - O2::ITSMFTReconstruction - O2::ITSMFTBase - LABELS its) - # FIXME: move to subsystem dir o2_add_test_root_macro(run_clus_tof.C PUBLIC_LINK_LIBRARIES O2::TOFReconstruction O2::Framework O2::TOFBase @@ -332,20 +326,6 @@ o2_add_test_root_macro(CreateCTPOrbitResetObject.C # O2::DataFormatsITSMFT O2::DataFormatsParameters O2::DetectorsBase O2::Field # O2::ITSBase O2::ITStracking O2::MathUtils O2::SimulationDataFormat) -# FIXME: move to subsystem dir -o2_add_test_root_macro(run_trac_its.C - PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats - O2::DataFormatsITSMFT - O2::DataFormatsParameters - O2::DetectorsBase - O2::Field - O2::ITSBase - O2::ITSReconstruction - O2::ITStracking - O2::MathUtils - O2::SimulationDataFormat - LABELS its) - o2_add_test_root_macro(CreateGRPECSObject.C PUBLIC_LINK_LIBRARIES O2::DataFormatsParameters O2::DetectorsCommonDataFormats @@ -459,18 +439,6 @@ o2_add_test_root_macro(getTimeStamp.C # finished succesfully) set_tests_properties(run_clus_its_G3 PROPERTIES DEPENDS # run_digi_its_G3) -# configure_file(${CMAKE_SOURCE_DIR}/macro/run_trac_its.sh -# ${CMAKE_BINARY_DIR}/macro/run_trac_its.sh) -# configure_file(${CMAKE_SOURCE_DIR}/macro/run_trac_its.C -# ${CMAKE_BINARY_DIR}/macro/run_trac_its.C) - -# add_test_wrap(NAME run_trac_its_G3 COMMAND -# ${CMAKE_BINARY_DIR}/macro/run_trac_its.sh 10 TGeant3) -# set_tests_properties(run_trac_its_G3 PROPERTIES TIMEOUT 30) -# set_tests_properties(run_trac_its_G3 PROPERTIES PASS_REGULAR_EXPRESSION Macro -# finished succesfully) set_tests_properties(run_trac_its_G3 PROPERTIES DEPENDS -# run_clus_its_G3) - # #ITS tests with G4 # add_test_wrap(NAME run_sim_its_G4 COMMAND @@ -493,13 +461,6 @@ o2_add_test_root_macro(getTimeStamp.C # finished succesfully) set_tests_properties(run_clus_its_G4 PROPERTIES DEPENDS # run_digi_its_G4) -# add_test_wrap(NAME run_trac_its_G4 COMMAND -# ${CMAKE_BINARY_DIR}/macro/run_trac_its.sh 10 TGeant4) -# set_tests_properties(run_trac_its_G4 PROPERTIES TIMEOUT 30) -# set_tests_properties(run_trac_its_G4 PROPERTIES PASS_REGULAR_EXPRESSION Macro -# finished succesfully) set_tests_properties(run_trac_its_G4 PROPERTIES DEPENDS -# run_clus_its_G4) - # GENERATE_ROOT_TEST_SCRIPT(${CMAKE_SOURCE_DIR}/macro/load_all_libs.C) # add_test_wrap(load_all_libs ${CMAKE_BINARY_DIR}/macro/load_all_libs.sh) # Set_Tests_Properties(load_all_libs PROPERTIES TIMEOUT 30) diff --git a/macro/build_geometry.C b/macro/build_geometry.C index fde043256046a..ccc3b13fe728d 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -56,12 +56,15 @@ #include #include #include +#include #include #include #include #include #endif +#include + using Return = o2::base::Detector*; void finalize_geometry(FairRunSim* run); @@ -181,6 +184,18 @@ void build_geometry(FairRunSim* run = nullptr) } #endif + if (isActivated("EXT")) { + // EXAMPLE!! how to pick geometry generated from external (CAD) module via `O2_CADtoTGeo.py` + o2::passive::ExternalModuleOptions options; + options.root_macro_file = "PATH_TO_EXTERNAL_GEOM_MODULE/geom.C"; + options.anchor_volume = "barrel"; // hook this into barrel + auto rot = new TGeoCombiTrans(); + rot->RotateX(90); + rot->SetDy(30); // we need to compensate for a shift of barrel with respect to zero + options.placement = rot; + run->AddModule(new o2::passive::ExternalModule("FOO", "BAR", options)); + } + // the absorber if (isActivated("ABSO")) { // the frame structure to support other detectors @@ -264,6 +279,11 @@ void build_geometry(FairRunSim* run = nullptr) addReadoutDetector(new o2::ecal::Detector(isReadout("ECL"))); } + if (isActivated("FD3")) { + // ALICE3 FD3 + addReadoutDetector(new o2::fd3::Detector(isReadout("FD3"))); + } + if (isActivated("MI3")) { // ALICE 3 MID addReadoutDetector(new o2::mi3::Detector(isReadout("MI3"))); diff --git a/macro/convert_onnx_to_root_serialized.C b/macro/convert_onnx_to_root_serialized.C new file mode 100644 index 0000000000000..b1b8b981393a1 --- /dev/null +++ b/macro/convert_onnx_to_root_serialized.C @@ -0,0 +1,220 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file convert_onnx_to_root_serialized.C +/// \brief Utility functions to be executed as a ROOT macro for uploading ONNX models to CCDB as ROOT serialized objects and vice versa +/// \author Christian Sonnabend + +// Example execution: root -l -b -q '/scratch/csonnabe/MyO2/O2/GPU/GPUTracking/utils/convert_onnx_to_root_serialized.C("/scratch/csonnabe/PhD/jobs/clusterization/NN/output/21082025_smallWindow_clean/SC/training_data_21082025_reco_noise_supressed_p3t6_CoGselected/SC/PbPb_24arp2/0_5/class1/regression/399_noMom/network/net_fp16.onnx", "", 1, 1, "nnCCDBLayerType=FC/nnCCDBWithMomentum=0/inputDType=FP16/nnCCDBInteractionRate=500/outputDType=FP16/nnCCDBEvalType=regression_c1/nnCCDBBeamType=pp/partName=blob/quality=3", 1, 4108971600000, "Users/c/csonnabe/TPC/Clusterization", "model.root")' + +#include "ORTRootSerializer.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/CcdbObjectInfo.h" +#include "TFile.h" +#include +#include + +o2::tpc::ORTRootSerializer serializer; + +/// Dumps the char* to a .onnx file -> Directly readable by ONNX runtime or Netron +void dumpOnnxToFile(const char* modelBuffer, uint32_t size, const std::string outputPath) +{ + std::ofstream outFile(outputPath, std::ios::binary | std::ios::trunc); + if (!outFile.is_open()) { + throw std::runtime_error("Failed to open output ONNX file: " + outputPath); + } + outFile.write(modelBuffer, static_cast(size)); + if (!outFile) { + throw std::runtime_error("Failed while writing data to: " + outputPath); + } + outFile.close(); +} + +/// Initialize the serialization from an ONNX file +void readOnnxModelFromFile(const std::string modelPath) +{ + std::ifstream inFile(modelPath, std::ios::binary | std::ios::ate); + if (!inFile.is_open()) { + throw std::runtime_error("Could not open input ONNX file " + modelPath); + } + std::streamsize size = inFile.tellg(); + std::vector mModelBuffer(size); + inFile.seekg(0, std::ios::beg); + if (!inFile.read(mModelBuffer.data(), size)) { + throw std::runtime_error("Could not read input ONNX file " + modelPath); + } + inFile.close(); + serializer.setOnnxModel(mModelBuffer.data(), static_cast(size)); +} + +/// Initialize the serialization from a ROOT file +void readRootModelFromFile(const std::string rootFilePath, std::string key) +{ + TFile inRootFile(rootFilePath.c_str()); + if (inRootFile.IsZombie()) { + throw std::runtime_error("Could not open input ROOT file " + rootFilePath); + } + auto* serPtr = inRootFile.Get(key.c_str()); + if (!serPtr) { + throw std::runtime_error("Could not find " + key + " in ROOT file " + rootFilePath); + } + serializer = *serPtr; + inRootFile.Close(); +} + +/// Serialize the ONNX model to a ROOT object and store to file +void onnxToRoot(std::string infile, std::string outfile, std::string key) +{ + readOnnxModelFromFile(infile); + TFile outRootFile(outfile.c_str(), "RECREATE"); + if (outRootFile.IsZombie()) { + throw std::runtime_error("Could not create output ROOT file " + outfile); + } + outRootFile.WriteObject(&serializer, key.c_str()); + outRootFile.Close(); +} + +/// Deserialize the ONNX model from a ROOT object and store to a .onnx file +void rootToOnnx(std::string infile, std::string outfile, std::string key) +{ + TFile inRootFile(infile.c_str()); + if (inRootFile.IsZombie()) { + throw std::runtime_error("Could not open input ROOT file " + infile); + } + auto* serPtr = inRootFile.Get(key.c_str()); + if (!serPtr) { + throw std::runtime_error("Could not find " + key + " in ROOT file " + infile); + } + serializer = *serPtr; + + std::ofstream outFile(outfile, std::ios::binary | std::ios::trunc); + if (!outFile.is_open()) { + throw std::runtime_error("Failed to open output ONNX file: " + outfile); + } + outFile.write(serializer.getONNXModel(), static_cast(serializer.getONNXModelSize())); + if (!outFile) { + throw std::runtime_error("Failed while writing data to: " + outfile); + } + outFile.close(); + + inRootFile.Close(); +} + +/// Upload the ONNX model to CCDB from an ONNX file +/// !!! Adjust the metadata, path and validity !!! +void uploadToCCDBFromONNX(std::string onnxFile, + const std::map& metadata, + // { // some example metadata entries + // "nnCCDBLayerType": "FC", + // "nnCCDBWithMomentum": "0", + // "inputDType": "FP16", + // "nnCCDBInteractionRate": "500", + // "outputDType": "FP16", + // "nnCCDBEvalType": "regression_c1", + // "nnCCDBBeamType": "pp", + // "partName": "blob", + // "quality": "3" + // } + long tsMin /* = 1 */, + long tsMax /* = 4108971600000 */, + std::string ccdbPath /* = "Users/c/csonnabe/TPC/Clusterization" */, + std::string objname /* = "net_regression_r1.root" */, + std::string ccdbUrl /* = "http://alice-ccdb.cern.ch" */) +{ + readOnnxModelFromFile(onnxFile); + + o2::ccdb::CcdbApi api; + api.init(ccdbUrl); + + // build full CCDB path including filename + const std::string fullPath = ccdbPath; //.back() == '/' ? (ccdbPath + objname) : (ccdbPath + "/" + objname); + + api.storeAsTFileAny(&serializer, fullPath, metadata, tsMin, tsMax); +} + +/// Upload the ONNX model to CCDB from a ROOT file +/// !!! Adjust the metadata, path and validity !!! +void uploadToCCDBFromROOT(std::string rootFile, + const std::map& metadata, + long tsMin /* = 1 */, + long tsMax /* = 4108971600000 */, + std::string ccdbPath /* = "Users/c/csonnabe/TPC/Clusterization" */, + std::string objname /* = "net_regression_r1.root" */, + std::string ccdbUrl /* = "http://alice-ccdb.cern.ch" */) +{ + // read ROOT file, extract ORTRootSerializer object and upload via storeAsTFileAny + TFile inRootFile(rootFile.c_str()); + if (inRootFile.IsZombie()) { + throw std::runtime_error("Could not open input ROOT file " + rootFile); + } + + // if objname is empty, fall back to default CCDB object key + const std::string key = objname.empty() ? o2::ccdb::CcdbApi::CCDBOBJECT_ENTRY : objname; + + auto* serPtr = inRootFile.Get(key.c_str()); + if (!serPtr) { + inRootFile.Close(); + throw std::runtime_error("Could not find " + key + " in ROOT file " + rootFile); + } + serializer = *serPtr; + + o2::ccdb::CcdbApi api; + api.init(ccdbUrl); + + // build full CCDB path including filename + const std::string fullPath = ccdbPath; //.back() == '/' ? (ccdbPath + objname) : (ccdbPath + "/" + objname); + + api.storeAsTFileAny(&serializer, fullPath, metadata, tsMin, tsMax); + + inRootFile.Close(); +} + +void convert_onnx_to_root_serialized(const std::string& onnxFile, + const std::string& rootFile, + int mode = 0, + int ccdbUpload = 0, + const std::string& metadataStr = "nnCCDBLayerType=FC/nnCCDBWithMomentum=0/inputDType=FP16/nnCCDBInteractionRate=500/outputDType=FP16/nnCCDBEvalType=regression_c1/nnCCDBBeamType=pp/partName=blob/quality=3", + long tsMin = 1, + long tsMax = 4108971600000, + std::string ccdbPath = "Users/c/csonnabe/TPC/Clusterization", + std::string objname = "net_regression_r1.root", + std::string ccdbUrl = "http://alice-ccdb.cern.ch") +{ + // parse metadataStr of the form key=value/key2=value2/... + std::map metadata; + std::size_t start = 0; + while (start < metadataStr.size()) { + auto sep = metadataStr.find('/', start); + auto token = metadataStr.substr(start, sep == std::string::npos ? std::string::npos : sep - start); + if (!token.empty()) { + auto eq = token.find('='); + if (eq != std::string::npos && eq > 0 && eq + 1 < token.size()) { + metadata.emplace(token.substr(0, eq), token.substr(eq + 1)); + } + } + if (sep == std::string::npos) { + break; + } + start = sep + 1; + } + + if (ccdbUpload == 0) { + if (mode == 0) + onnxToRoot(onnxFile, rootFile, o2::ccdb::CcdbApi::CCDBOBJECT_ENTRY); + else if (mode == 1) + rootToOnnx(rootFile, onnxFile, o2::ccdb::CcdbApi::CCDBOBJECT_ENTRY); + } else if (ccdbUpload == 1) { + if (mode == 0) + uploadToCCDBFromROOT(rootFile, metadata, tsMin, tsMax, ccdbPath, objname, ccdbUrl); + else if (mode == 1) + uploadToCCDBFromONNX(onnxFile, metadata, tsMin, tsMax, ccdbPath, objname, ccdbUrl); + } +} diff --git a/macro/o2sim.C b/macro/o2sim.C index 7d5faa544249c..fd44de6e93b54 100644 --- a/macro/o2sim.C +++ b/macro/o2sim.C @@ -69,6 +69,7 @@ FairRunSim* o2sim_init(bool asservice, bool evalmat = false) auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); // fix the timestamp early uint64_t timestamp = confref.getTimestamp(); + uint64_t runEnd = timestamp + 3600000; // see if we have a run number but not a timestamp auto run_number = confref.getRunNumber(); if (run_number != -1) { @@ -77,6 +78,7 @@ FairRunSim* o2sim_init(bool asservice, bool evalmat = false) auto [sor, eor] = ccdbmgr.getRunDuration(run_number); LOG(info) << "Have run number. Fixing timestamp to " << sor; timestamp = sor; + runEnd = eor; } } @@ -189,7 +191,7 @@ FairRunSim* o2sim_init(bool asservice, bool evalmat = false) } uint64_t runStart = timestamp; grp.setTimeStart(runStart); - grp.setTimeEnd(runStart + 3600000); + grp.setTimeEnd(runEnd); grp.setDetsReadOut(readoutDetMask); // CTP is not a physical detector, just flag in the GRP if requested if (isReadout("CTP")) { @@ -216,7 +218,7 @@ FairRunSim* o2sim_init(bool asservice, bool evalmat = false) o2::parameters::GRPECSObject grp; grp.setRun(run->GetRunId()); grp.setTimeStart(runStart); - grp.setTimeEnd(runStart + 3600000); + grp.setTimeEnd(runEnd); grp.setNHBFPerTF(128); // might be overridden later grp.setDetsReadOut(readoutDetMask); if (isReadout("CTP")) { diff --git a/macro/run_clus_itsSA.C b/macro/run_clus_itsSA.C deleted file mode 100644 index a96cd66d5eeec..0000000000000 --- a/macro/run_clus_itsSA.C +++ /dev/null @@ -1,65 +0,0 @@ -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include -#include "DetectorsBase/GeometryManager.h" -#include "ITSReconstruction/ClustererTask.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include -#include "CCDB/BasicCCDBManager.h" -#include "CCDB/CCDBTimeStampUtils.h" -#endif - -// Clusterization avoiding FairRunAna management. -// Works both with MC digits and with "raw" data (in this case the last argument must be -// set to true). The raw data should be prepared beforeahand from the MC digits using e.g. -// o2::itsmft::RawPixelReader reader; -// reader.convertDigits2Raw("dig.raw","o2dig.root","o2sim","ITSDigit"); -// -// Use for MC mode: -// root -b -q run_clus_itsSA.C+\(\"o2clus_its.root\",\"o2dig.root\"\) 2>&1 | tee clusSA.log -// -// Use for RAW mode: -// root -b -q run_clus_itsSA.C+\(\"o2clus_its.root\",\"dig.raw\"\) 2>&1 | tee clusSARAW.log -// - -void run_clus_itsSA(std::string inputfile = "rawits.bin", // input file name - std::string outputfile = "clr.root", // output file name (root or raw) - bool raw = true, // flag if this is raw data - int strobeBC = -1, // strobe length in BC for masking, if <0, get automatically (assume cont. readout) - long timestamp = 0, - bool withPatterns = true) -{ - // Initialize logger - FairLogger* logger = FairLogger::GetLogger(); - logger->SetLogVerbosityLevel("LOW"); - logger->SetLogScreenLevel("INFO"); - - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL("http://alice-ccdb.cern.ch"); - mgr.setTimestamp(timestamp ? timestamp : o2::ccdb::getCurrentTimestamp()); - const o2::itsmft::TopologyDictionary* dict = mgr.get("ITS/Calib/ClusterDictionary"); - - TStopwatch timer; - - // Setup clusterizer - Bool_t useMCTruth = kTRUE; // kFALSE if no comparison with MC needed - o2::its::ClustererTask* clus = new o2::its::ClustererTask(useMCTruth, raw); - clus->setMaxROframe(2 << 21); // about 3 cluster files per a raw data chunk - clus->getClusterer().setDictionary(dict); - - // Mask fired pixels separated by <= this number of BCs (for overflow pixels). - // In continuos mode strobe lenght should be used, in triggered one: signal shaping time (~7mus) - if (strobeBC < 0) { - const auto& dgParams = o2::itsmft::DPLAlpideParam::Instance(); - strobeBC = dgParams.roFrameLengthInBC; - } - clus->getClusterer().setMaxBCSeparationToMask(strobeBC + 10); - - clus->getClusterer().print(); - clus->run(inputfile, outputfile); - - timer.Stop(); - timer.Print(); -} diff --git a/macro/run_trac_its.C b/macro/run_trac_its.C deleted file mode 100644 index 824e4ebcf5d79..0000000000000 --- a/macro/run_trac_its.C +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include "Framework/Logger.h" -#include "DetectorsCommonDataFormats/DetID.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "DetectorsBase/GeometryManager.h" -#include "DetectorsBase/Propagator.h" -#include "Field/MagneticField.h" -#include "ITSBase/GeometryTGeo.h" -#include "ITSReconstruction/CookedTracker.h" -#include "MathUtils/Utils.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "ReconstructionDataFormats/Vertex.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "CCDB/BasicCCDBManager.h" -#include "CCDB/CCDBTimeStampUtils.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" - -#include "ReconstructionDataFormats/PrimaryVertex.h" // hack to silence JIT compiler -#include "ITStracking/ROframe.h" -#include "ITStracking/IOUtils.h" -#include "ITStracking/Vertexer.h" -#include "ITStracking/VertexerTraits.h" - -using MCLabCont = o2::dataformats::MCTruthContainer; -using MCLabContTr = std::vector; -using Vertex = o2::dataformats::Vertex>; - -void run_trac_its(std::string path = "./", std::string outputfile = "o2trac_its.root", - std::string inputClustersITS = "o2clus_its.root", - std::string inputGeom = "", - std::string inputGRP = "o2sim_grp.root", - long timestamp = 0) -{ - - // Setup timer - TStopwatch timer; - - if (path.back() != '/') { - path += '/'; - } - - //-------- init geometry and field --------// - const auto grp = o2::parameters::GRPObject::loadFrom(path + inputGRP); - if (!grp) { - LOG(fatal) << "Cannot run w/o GRP object"; - } - bool isITS = grp->isDetReadOut(o2::detectors::DetID::ITS); - if (!isITS) { - LOG(warning) << "ITS is not in the readoute"; - return; - } - bool isContITS = grp->isDetContinuousReadOut(o2::detectors::DetID::ITS); - LOG(info) << "ITS is in " << (isContITS ? "CONTINUOS" : "TRIGGERED") << " readout mode"; - - o2::base::GeometryManager::loadGeometry(inputGeom); - auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)); // request cached transforms - - o2::base::Propagator::initFieldFromGRP(grp); - auto field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - if (!field) { - LOG(fatal) << "Failed to load ma"; - } - - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL("http://alice-ccdb.cern.ch"); - mgr.setTimestamp(timestamp ? timestamp : o2::ccdb::getCurrentTimestamp()); - const o2::itsmft::TopologyDictionary* dict = mgr.get("ITS/Calib/ClusterDictionary"); - - //>>>---------- attach input data --------------->>> - TChain itsClusters("o2sim"); - itsClusters.AddFile((path + inputClustersITS).data()); - - if (!itsClusters.GetBranch("ITSClusterComp")) { - LOG(fatal) << "Did not find ITS clusters branch ITSClusterComp in the input tree"; - } - std::vector* cclusters = nullptr; - itsClusters.SetBranchAddress("ITSClusterComp", &cclusters); - - if (!itsClusters.GetBranch("ITSClusterPatt")) { - LOG(fatal) << "Did not find ITS cluster patterns branch ITSClusterPatt in the input tree"; - } - std::vector* patterns = nullptr; - itsClusters.SetBranchAddress("ITSClusterPatt", &patterns); - - MCLabCont* labels = nullptr; - if (!itsClusters.GetBranch("ITSClusterMCTruth")) { - LOG(warning) << "Did not find ITS clusters branch ITSClusterMCTruth in the input tree"; - } else { - itsClusters.SetBranchAddress("ITSClusterMCTruth", &labels); - } - - if (!itsClusters.GetBranch("ITSClustersROF")) { - LOG(fatal) << "Did not find ITS clusters branch ITSClustersROF in the input tree"; - } - - std::vector* mc2rofs = nullptr; - if (!itsClusters.GetBranch("ITSClustersMC2ROF")) { - LOG(warning) << "Did not find ITSClustersMC2ROF branch in the input tree"; - } - itsClusters.SetBranchAddress("ITSClustersMC2ROF", &mc2rofs); - - std::vector* rofs = nullptr; - itsClusters.SetBranchAddress("ITSClustersROF", &rofs); - - //>>>--------- create/attach output ------------->>> - // create/attach output tree - TFile outFile((path + outputfile).data(), "recreate"); - TTree outTree("o2sim", "Cooked ITS Tracks"); - std::vector tracksITS, *tracksITSPtr = &tracksITS; - std::vector trackClIdx, *trackClIdxPtr = &trackClIdx; - std::vector vertROFvec, *vertROFvecPtr = &vertROFvec; - std::vector vertices, *verticesPtr = &vertices; - - MCLabContTr trackLabels, *trackLabelsPtr = &trackLabels; - outTree.Branch("ITSTrack", &tracksITSPtr); - outTree.Branch("ITSTrackClusIdx", &trackClIdxPtr); - outTree.Branch("ITSTrackMCTruth", &trackLabelsPtr); - outTree.Branch("ITSTracksROF", &rofs); - outTree.Branch("ITSTracksMC2ROF", &mc2rofs); - outTree.Branch("Vertices", &verticesPtr); - outTree.Branch("VerticesROF", &vertROFvecPtr); - //<<<--------- create/attach output -------------<<< - - //=================== INIT ================== - Int_t n = 1; // Number of threads - Bool_t mcTruth = kTRUE; // kFALSE if no comparison with MC is needed - o2::its::CookedTracker tracker(n); - tracker.setContinuousMode(isContITS); - tracker.setBz(field->solenoidField()); // in kG - tracker.setGeometry(gman); - if (mcTruth) { - tracker.setMCTruthContainers(labels, trackLabelsPtr); - } - //=========================================== - - o2::its::VertexerTraits vertexerTraits; - o2::its::Vertexer vertexer(&vertexerTraits); - - int nTFs = itsClusters.GetEntries(); - for (int nt = 0; nt < nTFs; nt++) { - LOGP(info, "Processing timeframe {}/{}", nt, nTFs); - itsClusters.GetEntry(nt); - o2::its::TimeFrame tf; - gsl::span rofspan(*rofs); - gsl::span patt(*patterns); - - auto pattIt = patt.begin(); - auto pattIt_vertexer = patt.begin(); - auto clSpan = gsl::span(cclusters->data(), cclusters->size()); - std::vector processingMask(rofs->size(), true); - tf.loadROFrameData(rofspan, clSpan, pattIt_vertexer, dict, labels); - tf.setMultiplicityCutMask(processingMask); - vertexer.adoptTimeFrame(tf); - vertexer.clustersToVertices(); - int iRof = 0; - for (auto& rof : *rofs) { - auto it = pattIt; - - auto& vtxROF = vertROFvec.emplace_back(rof); // register entry and number of vertices in the - vtxROF.setFirstEntry(vertices.size()); // dedicated ROFRecord - std::vector>> verticesL; - vtxROF.setNEntries(tf.getPrimaryVertices(iRof).size()); - - for (const auto& vtx : tf.getPrimaryVertices(iRof)) { - vertices.push_back(vtx); - verticesL.push_back(vtx); - } - if (tf.getPrimaryVertices(iRof).empty()) { - verticesL.emplace_back(); - } - tracker.setVertices(verticesL); - tracker.process(clSpan, it, dict, tracksITS, trackClIdx, rof); - ++iRof; - } - outTree.Fill(); - if (mcTruth) { - trackLabelsPtr->clear(); - mc2rofs->clear(); - } - tracksITSPtr->clear(); - trackClIdxPtr->clear(); - rofs->clear(); - verticesPtr->clear(); - vertROFvecPtr->clear(); - } - outFile.cd(); - outTree.Write(); - outFile.Close(); - - timer.Stop(); - timer.Print(); -} - -#endif diff --git a/prodtests/full-system-test/aggregator-workflow.sh b/prodtests/full-system-test/aggregator-workflow.sh index f90decefa3f2f..a0d091a98d193 100755 --- a/prodtests/full-system-test/aggregator-workflow.sh +++ b/prodtests/full-system-test/aggregator-workflow.sh @@ -14,6 +14,9 @@ source $O2DPG_ROOT/DATA/common/setenv.sh || { echo "setenv.sh failed" 1>&2 && ex source $O2DPG_ROOT/DATA/common/getCommonArgs.sh || { echo "getCommonArgs.sh failed" 1>&2 && exit 1; } source $O2DPG_ROOT/DATA/common/setenv_calib.sh || { echo "setenv_calib.sh failed" 1>&2 && exit 1; } +# print an error (instead of warning) when exit transition timer expires, only for tasks on aggregator nodes +ARGS_ALL+=" --error-on-exit-transition-timeout" + # if the populator for DCS CCDB is needed, set it to non-0 : ${NEED_DCS_CCDB_POPULATOR:=0} diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 2f0e761366e18..a8f01a3ef1822 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -106,7 +106,8 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" : ${ITSTPC_CONFIG_KEY:=} : ${AOD_SOURCES:=$TRACK_SOURCES} : ${AODPROD_OPT:=} -: ${ALPIDE_ERR_DUMPS:=0} +: ${ALPIDE_ERR_DUMPS:=} +[[ -z $ALPIDE_ERR_DUMPS ]] && [[ $EPNSYNCMODE == 1 && $RUNTYPE == "PHYSICS" ]] && ALPIDE_ERR_DUMPS=1 || ALPIDE_ERR_DUMPS=0 [[ "0$DISABLE_ROOT_OUTPUT" == "00" ]] && DISABLE_ROOT_OUTPUT= @@ -127,13 +128,13 @@ if [[ $SYNCMODE == 1 ]]; then fi [[ -n ${CUT_RANDOM_FRACTION_ITS:-} ]] && ITS_CONFIG_KEY+="fastMultConfig.cutRandomFraction=$CUT_RANDOM_FRACTION_ITS;" ITS_CONFIG_KEY+="ITSCATrackerParam.trackletsPerClusterLimit=${CUT_TRACKLETSPERCLUSTER_MAX_ITS:--1};ITSCATrackerParam.cellsPerClusterLimit=${CUT_CELLSPERCLUSTER_MAX_ITS:--1};" - if has_detector_reco ITS; then - [[ $RUNTYPE != "COSMICS" ]] && MFT_CONFIG_KEY+="MFTTracking.irFramesOnly=1;" + if has_detector_reco ITS && [[ $RUNTYPE != "COSMICS" && x"${MFT_DISABLE_ITS_IRFRAMES_SELECTION:-}" != "x1" ]]; then + MFT_CONFIG_KEY+="MFTTracking.irFramesOnly=1;" fi PVERTEXING_CONFIG_KEY+="pvertexer.meanVertexExtraErrConstraint=0.3;" # for calibration relax the constraint if [[ $SYNCRAWMODE == 1 ]]; then # add extra tolerance in sync mode to account for eventual time misalignment - PVERTEXING_CONFIG_KEY+="pvertexer.timeMarginVertexTime=2.5;" + [[ $BEAMTYPE == "pp" ]] && PVERTEXING_CONFIG_KEY+="pvertexer.timeMarginVertexTime=5;" || PVERTEXING_CONFIG_KEY+="pvertexer.timeMarginVertexTime=2.5;" if [[ -z $ITSEXTRAERR ]]; then # in sync mode account for ITS residual misalignment ERRIB="100e-8" ERROB="100e-8" @@ -145,6 +146,8 @@ if [[ $SYNCMODE == 1 ]]; then fi fi GPU_CONFIG_KEY+="GPU_global.synchronousProcessing=1;GPU_proc.clearO2OutputFromGPU=1;" + # relaxed cuts also used for async reconstruction, they require scaling of the GPU memory + GPU_CONFIG_KEY+="GPU_rec_tpc.trackletMinSharedNormFactor=1.;GPU_rec_tpc.trackletMaxSharedFraction=0.3;GPU_rec_tpc.rejectIFCLowRadiusCluster=1;GPU_rec_tpc.extrapolationTrackingRowRange=100;GPU_rec_tpc.clusterError2AdditionalYSeeding=0.1;GPU_rec_tpc.clusterError2AdditionalZSeeding=0.15;GPU_proc.memoryScalingFactor=1.2;" has_processing_step TPC_DEDX && GPU_CONFIG_KEY+="GPU_global.rundEdx=1;" has_detector ITS && TRD_FILTER_CONFIG+=" --filter-trigrec" else @@ -235,6 +238,9 @@ if [[ $EPNSYNCMODE == 1 ]]; then fi fi fi +if [[ $GPUTYPE != "CPU" && $NGPUS > 1 ]]; then + GPU_CONFIG_KEY+="GPU_global.dumpFolder=gpu_dump_[P];" +fi if [[ $SYNCRAWMODE == 1 ]]; then GPU_CONFIG_KEY+="GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;GPU_proc.throttleAlarms=1;" if [[ $RUNTYPE == "PHYSICS" || $RUNTYPE == "COSMICS" || $RUNTYPE == "TECHNICAL" ]]; then @@ -281,8 +287,6 @@ if [[ $GPUTYPE == "HIP" ]]; then if [[ ${EPN_NODE_MI100:-} == "1" && ${DISABLE_MI100_SERIALIZATION:-0} != 1 ]]; then if [[ -n ${OPTIMIZED_PARALLEL_ASYNC:-} ]] || [[ $EPNSYNCMODE == 1 && ${FULL_MI100_SERIALIZATION:-0} == 1 ]]; then GPU_CONFIG_KEY+="GPU_proc.serializeGPU=3;" - elif [[ $EPNSYNCMODE == 1 ]]; then - GPU_CONFIG_KEY+="GPU_proc.amdMI100SerializationWorkaround=1;" fi fi #export HSA_TOOLS_LIB=/opt/rocm/lib/librocm-debug-agent.so.2 @@ -376,7 +380,12 @@ if has_processing_step MUON_SYNC_RECO; then elif [[ $RUNTYPE == "PHYSICS" && $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]] || [[ $RUNTYPE == "COSMICS" ]]; then MCH_CONFIG_KEY+="MCHTracking.chamberResolutionX=0.4;MCHTracking.chamberResolutionY=0.4;MCHTracking.sigmaCutForTracking=7.;MCHTracking.sigmaCutForImprovement=6.;" fi - has_detector_reco ITS && [[ $RUNTYPE != "COSMICS" ]] && MCH_CONFIG_KEY+="MCHTimeClusterizer.irFramesOnly=true;" + if has_detector_reco ITS && [[ $RUNTYPE != "COSMICS" && x"${MCH_DISABLE_ITS_IRFRAMES_SELECTION:-}" != "x1" ]]; then + MCH_CONFIG_KEY+="MCHTimeClusterizer.irFramesOnly=true;" + [[ -z ${CUT_RANDOM_FRACTION_MCH:-} && -n ${CUT_RANDOM_FRACTION_MCH_WITH_ITS:-} ]] && CUT_RANDOM_FRACTION_MCH=${CUT_RANDOM_FRACTION_MCH_WITH_ITS:-} + else + [[ -z ${CUT_RANDOM_FRACTION_MCH:-} && -n ${CUT_RANDOM_FRACTION_MCH_NO_ITS:-} ]] && CUT_RANDOM_FRACTION_MCH=${CUT_RANDOM_FRACTION_MCH_NO_ITS:-} + fi [[ -n ${CUT_RANDOM_FRACTION_MCH:-} ]] && MCH_CONFIG_KEY+="MCHTimeClusterizer.rofRejectionFraction=$CUT_RANDOM_FRACTION_MCH;" MCH_CONFIG_KEY+="MCHStatusMap.useHV=false;MCHDigitFilter.statusMask=3;" [[ $RUNTYPE == "COSMICS" ]] && [[ -z ${CONFIG_EXTRA_PROCESS_o2_mft_reco_workflow:-} ]] && CONFIG_EXTRA_PROCESS_o2_mft_reco_workflow="MFTTracking.FullClusterScan=true" @@ -563,6 +572,9 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then fi has_detector_gpu ITS && GPU_INPUT+=",its-clusters" +if [[ $BEAMTYPE != "cosmic" && $SYNCMODE != 1 ]]; then + has_detector_gpu ITS && GPU_INPUT+=",its-mean-vertex" +fi has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- @@ -620,6 +632,12 @@ has_detector_reco ITS && has_detector_gpu ITS TPC && [[ -z "$DISABLE_ROOT_OUTPUT ( [[ $BEAMTYPE == "cosmic" ]] || ! has_detector_reco ITS) && PVERTEX_CONFIG+=" --skip" has_detector_matching PRIMVTX && [[ -n "$VERTEXING_SOURCES" ]] && [[ $GLOBAL_READER_NEEDS_PV != 1 ]] && add_W o2-primary-vertexing-workflow "$DISABLE_MC $DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $PVERTEX_CONFIG --pipeline $(get_N primary-vertexing MATCH REST 1 PRIMVTX),$(get_N pvertex-track-matching MATCH REST 1 PRIMVTXMATCH)" "${PVERTEXING_CONFIG_KEY};${INTERACTION_TAG_CONFIG_KEY};" +if [[ -z ${SVERTEXING_SOURCES:-} ]]; then + [[ $SYNCMODE == 1 ]] && [[ -n $TRACK_SOURCES_GLO ]] && SVERTEXING_SOURCES="$TRACK_SOURCES_GLO" || SVERTEXING_SOURCES="$VERTEXING_SOURCES" +elif [[ "${SVERTEXING_SOURCES^^}" == "NONE" ]]; then + SVERTEXING_SOURCES= +fi + if [[ $BEAMTYPE != "cosmic" ]] && has_detectors_reco ITS && has_detector_matching SECVTX && [[ -n "$SVERTEXING_SOURCES" ]]; then : ${REDUCESV_OPT:=} : ${REDUCESV_CONF:=} diff --git a/prodtests/full-system-test/start_tmux.sh b/prodtests/full-system-test/start_tmux.sh index 22b603f48a0d0..fb69cc1e6baec 100755 --- a/prodtests/full-system-test/start_tmux.sh +++ b/prodtests/full-system-test/start_tmux.sh @@ -1,5 +1,39 @@ #!/bin/bash +### --- Early safety checks ---------------------------------------------------- + +# Skip checks if FST_RUN_WITHOUT_CHECKS=1 +if [[ "${FST_RUN_WITHOUT_CHECKS:-0}" != "1" ]]; then + + # 1. Abort if running inside a Slurm shell + if [[ -n "${SLURM_JOB_ID:-}" ]]; then + echo "ERROR: This script must not be run inside a Slurm job (SLURM_JOB_ID=${SLURM_JOB_ID})." >&2 + echo "Please run it from a normal ssh shell." >&2 + exit 1 + fi + + # 2. Abort if FMQ shared-memory files exist in /dev/shm + if compgen -G "/dev/shm/fmq*" > /dev/null; then + echo "ERROR: Found existing /dev/shm/fmq* files." >&2 + echo "Please clean them manually before running the FST." >&2 + exit 1 + fi + + # 3. MI100 check: detect MI100 GPU but EPN_NODE_MI100 not set or set to 0 + if lspci | grep -qi "MI100"; then + if [[ -z "${EPN_NODE_MI100:-}" || "${EPN_NODE_MI100}" == "0" ]]; then + echo "ERROR: MI100 GPU detected on this node, but EPN_NODE_MI100 is not set to 1." >&2 + echo "Please export EPN_NODE_MI100=1 before running this script." >&2 + echo "See installation instructions here:" >&2 + echo " https://alice-pdp-operations.docs.cern.ch/o2install/#install-and-validate-the-new-o2pdpsuite-on-one-production-epn-using-the-fst" + exit 1 + fi + fi + +fi + +### --------------------------------------------------------------------------- + if [ "0$1" != "0dd" ] && [ "0$1" != "0rr" ] && [ "0$1" != "0tf" ]; then echo Please indicate whether to start with raw-reader [rr] or with DataDistribution [dd] or TfReader [tf] 1>&2 exit 1 @@ -57,6 +91,7 @@ export DATADIST_NEW_DPL_CHAN=1 [[ -z $GEN_TOPO_MYDIR ]] && GEN_TOPO_MYDIR="$(dirname $(realpath $0))" source $GEN_TOPO_MYDIR/setenv.sh || { echo "setenv.sh failed" 1>&2 && exit 1; } +mkdir -p $EDJSONS_DIR # create event display directory to avoid filesystem error messages workflow_has_parameter QC && export QC_REDIRECT_MERGER_TO_LOCALHOST=1 diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 6100e3ed87a2c..07ccdf01d4566 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -31,7 +31,7 @@ fi # --> the taskwrapper as a simple control and monitoring tool # (look inside the jobutils.sh file for documentation) # --> utilities to query CPU count -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh # make sure that correct format will be used irrespecive of the locale export LC_NUMERIC=C @@ -40,6 +40,7 @@ export LC_ALL=C BEAMTYPE=${BEAMTYPE:-PbPb} NEvents=${NEvents:-10} #550 for full TF (the number of PbPb events) NEventsQED=${NEventsQED:-1000} #35000 for full TF +OrbitsBeforeTf=${OrbitsBeforeTf:-1} NCPUS=$(getNumberOfPhysicalCPUCores) echo "Found ${NCPUS} physical CPU cores" NJOBS=${NJOBS:-"${NCPUS}"} @@ -54,8 +55,8 @@ O2SIMSEED=${O2SIMSEED:-0} SPLITTRDDIGI=${SPLITTRDDIGI:-1} DIGITDOWNSCALINGTRD=${DIGITDOWNSCALINGTRD:-1000} NHBPERTF=${NHBPERTF:-128} -RUNFIRSTORBIT=${RUNFIRSTORBIT:-0} -FIRSTSAMPLEDORBIT=${FIRSTSAMPLEDORBIT:-0} +RUNFIRSTORBIT=${RUNFIRSTORBIT:-256} +FIRSTSAMPLEDORBIT=${FIRSTSAMPLEDORBIT:-256} OBLIGATORYSOR=${OBLIGATORYSOR:-false} FST_TPC_ZSVERSION=${FST_TPC_ZSVERSION:-4} TPC_SLOW_REALISITC_FULL_SIM=${TPC_SLOW_REALISITC_FULL_SIM:-0} @@ -137,11 +138,47 @@ if [[ $TPC_SLOW_REALISITC_FULL_SIM == 1 ]]; then DIGITOPTKEY+="TPCEleParam.doCommonModePerPad=0;TPCEleParam.doIonTailPerPad=1;TPCEleParam.commonModeCoupling=0;TPCEleParam.doNoiseEmptyPads=1;TPCEleParam.doSaturationTail=0;TPCDetParam.TPCRecoWindowSim=10;" fi -taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim +# Create collision context +SIGNALSPEC="o2sim,${FST_COLRATE},1000000:1000000" +QEDSPEC="" +if [[ $FST_QED == 1 ]]; then + PbPbXSec="8." + QEDXSECRATIO=$(awk "BEGIN {printf \"%.2f\",`grep xSectionQED qed/qedgenparam.ini | cut -d'=' -f 2`/$PbPbXSec}") + QEDRATE=$(awk "BEGIN {printf \"%.2f\",${FST_COLRATE}*${QEDXSECRATIO}}") + QEDSPEC="--QEDinteraction qed,${QEDRATE},10000000:${NEventsQED}" +fi + +taskwrapper collcontext.log o2-steer-colcontexttool \ + -i ${SIGNALSPEC} \ + --show-context \ + --timeframeID 0 \ + --orbitsPerTF ${NHBPERTF} \ + --orbits $(( ${NTIMEFRAMES} * ${NHBPERTF} )) \ + --seed ${O2SIMSEED} \ + --noEmptyTF \ + --first-orbit ${RUNFIRSTORBIT} \ + --extract-per-timeframe tf:o2sim \ + --with-vertices kCCDB \ + --maxCollsPerTF ${NEvents} \ + --orbitsEarly ${OrbitsBeforeTf} \ + --bcPatternFile ccdb \ + ${QEDSPEC} + +# Include collision system for TPC loopers generation +SIMOPTKEY+="GenTPCLoopers.colsys=${BEAMTYPE};" + +taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --vertexMode kCollContext --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim --fromCollContext collisioncontext.root:o2sim +# Test MCTracks to AO2D conversion tool +taskwrapper kine2aod.log "o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe $NEvents | o2-sim-mctracks-to-aod -b --aod-writer-keep dangling | o2-analysis-mctracks-to-aod-simple-task -b" +if [[ ! -s AnalysisResults_trees.root ]] || [[ ! -s AnalysisResults.root ]]; then + echo "Error: AnalysisResults_trees.root (AO2D from Kine file) or AnalysisResults.root (simple analysis task output) missing or empty" + exit 1 +fi + if [[ $DO_EMBEDDING == 1 ]]; then - taskwrapper embed.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} -j $NJOBS --run ${RUNNUMBER} -n $NEvents -g pythia8pp -e ${FST_MC_ENGINE} -o sig --configKeyValues ${FST_EMBEDDING_CONFIG} --embedIntoFile o2sim_Kine.root + taskwrapper embed.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} -j $NJOBS --run ${RUNNUMBER} -n $NEvents -g pythia8pp -e ${FST_MC_ENGINE} -o sig --configKeyValues ${FST_EMBEDDING_CONFIG} --embedIntoFile o2sim_MCHeader.root fi -taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents ${DIGIQED} ${NOMCLABELS} --sims ${SIM_SOURCES} --tpc-lanes $((NJOBS < 36 ? NJOBS : 36)) --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} ${DIGITOPT} --configKeyValues "\"${DIGITOPTKEY}\"" --interactionRate $FST_COLRATE --early-forward-policy always +taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents ${DIGIQED} ${NOMCLABELS} --sims ${SIM_SOURCES} --tpc-lanes $((NJOBS < 36 ? NJOBS : 36)) --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} ${DIGITOPT} --configKeyValues "\"${DIGITOPTKEY}\"" --interactionRate $FST_COLRATE --early-forward-policy always --incontext collisioncontext.root [[ $SPLITTRDDIGI == "1" ]] && taskwrapper digiTRD.log o2-sim-digitizer-workflow -n $NEvents ${NOMCLABELS} --sims ${SIM_SOURCES} --onlyDet TRD --trd-digit-downscaling ${DIGITDOWNSCALINGTRD} --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} --incontext collisioncontext.root --configKeyValues "\"${DIGITOPTKEYTRD}\"" --early-forward-policy always touch digiTRD.log_done @@ -295,6 +332,25 @@ for STAGE in $STAGES; do # boolean flag indicating if workflow completed successfully at all RC=$? SUCCESS=0 + # Check AOD production for ASYNC stage + if [[ "$STAGE" = "ASYNC" ]]; then + if [[ -f "AO2D.root" ]]; then + aod_size=`stat -c%s AO2D.root` + if [[ $aod_size -gt 0 ]]; then + echo "AO2D file produced: AO2D.root (size: ${aod_size} bytes)" + echo "aod_size_${STAGE},${TAG} value=${aod_size}" >> ${METRICFILE} + else + echo "ERROR: AO2D file (AO2D.root) exists but is empty" + echo "aod_size_${STAGE},${TAG} value=0" >> ${METRICFILE} + exit 1 + fi + else + echo "ERROR: AO2D file (AO2D.root) was not produced in ASYNC stage" + echo "aod_size_${STAGE},${TAG} value=0" >> ${METRICFILE} + exit 1 + fi + fi + [[ -f "${logfile}_done" ]] && [[ "$RC" = 0 ]] && SUCCESS=1 echo "success_${STAGE},${TAG} value=${SUCCESS}" >> ${METRICFILE} diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index fd43207f92d1e..474aa7e41eb7c 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -46,6 +46,7 @@ target_link_libraries(allsim $<$:O2::IOTOFSimulation> $<$:O2::RICHSimulation> $<$:O2::ECalSimulation> + $<$:O2::FD3Simulation> $<$:O2::MI3Simulation> O2::Generators) @@ -340,4 +341,4 @@ install(FILES o2-sim-client.py PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUT install(DIRECTORY SimExamples/ DESTINATION examples PATTERN * - PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ) \ No newline at end of file + PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ) diff --git a/run/O2HitMerger.h b/run/O2HitMerger.h index 520873e7aaafe..30ddd57ba91da 100644 --- a/run/O2HitMerger.h +++ b/run/O2HitMerger.h @@ -78,6 +78,7 @@ #include #include #include +#include #endif #include @@ -87,6 +88,29 @@ namespace o2 namespace devices { +// Function communicating to primary particle server that it is now safe to shutdown. +// From the perspective of o2-sim, this is the case when all configs have been propagated and the system +// is running ok: For instance after the HitMerger is initialized and got it's first data from Geant workers. +bool primaryServer_sendShutdownPermission(fair::mq::Channel& channel) +{ + std::unique_ptr request(channel.NewSimpleMessage((int)o2::O2PrimaryServerInfoRequest::AllowShutdown)); + std::unique_ptr reply(channel.NewMessage()); + + int timeoutinMS = 100; + if (channel.Send(request, timeoutinMS) > 0) { + LOG(info) << "Sending Shutdown permission to particle server"; + if (channel.Receive(reply, timeoutinMS) > 0) { + // the answer is a simple ack with a status code + LOG(info) << "Shutdown permission was acknowledged"; + } else { + LOG(error) << "No answer received within " << timeoutinMS << "ms\n"; + return false; + } + return true; + } + return false; +} + class O2HitMerger : public fair::mq::Device { @@ -128,6 +152,9 @@ class O2HitMerger : public fair::mq::Device if (o2::devices::O2SimDevice::querySimConfig(GetChannels().at("o2sim-primserv-info").at(0))) { outfilename = o2::base::NameConf::getMCKinematicsFileName(o2::conf::SimConfig::Instance().getOutPrefix().c_str()); mNExpectedEvents = o2::conf::SimConfig::Instance().getNEvents(); + } else { + // we didn't manage to get a configuration --> better to fail + LOG(fatal) << "No configuration received. Aborting"; } mAsService = o2::conf::SimConfig::Instance().asService(); mForwardKine = o2::conf::SimConfig::Instance().forwardKine(); @@ -353,6 +380,13 @@ class O2HitMerger : public fair::mq::Device // for the next batch return waitForControlInput(); } + + static bool initAcknowledged = false; + if (!initAcknowledged) { + primaryServer_sendShutdownPermission(GetChannels().at("o2sim-primserv-info").at(0)); + initAcknowledged = true; + } + return more; } @@ -412,10 +446,6 @@ class O2HitMerger : public fair::mq::Device }; } } - if (!expectmore) { - // somehow FairMQ has difficulties shutting down; helping manually - // raise(SIGINT); - } return expectmore; } @@ -1009,6 +1039,10 @@ void O2HitMerger::initDetInstances() mDetectorInstances[i] = std::move(std::make_unique(true)); counter++; } + if (i == DetID::FD3) { + mDetectorInstances[i] = std::move(std::make_unique(true)); + counter++; + } #endif } if (counter != DetID::nDetectors) { diff --git a/run/O2PrimaryServerDevice.h b/run/O2PrimaryServerDevice.h index ece3747b2c94a..b8703ffcddb28 100644 --- a/run/O2PrimaryServerDevice.h +++ b/run/O2PrimaryServerDevice.h @@ -46,6 +46,7 @@ #include #include #include +#include namespace o2 { @@ -135,6 +136,17 @@ class O2PrimaryServerDevice final : public fair::mq::Device auto embedinto_filename = conf.getEmbedIntoFileName(); if (!embedinto_filename.empty()) { + // determine the sim prefix from the embedding filename + // the filename should be an MCHeader file ... so it should match SOME_PATH/prefix_MCHeader.root + std::regex re(R"((.*/)?([^/]+)_MCHeader\.root$)"); + std::smatch match; + + if (std::regex_search(embedinto_filename, match, re)) { + std::cout << "Extracted embedding prefix : " << match[2] << '\n'; + mEmbeddIntoPrefix = match[2]; + } else { + LOG(fatal) << "Embedding asked but no suitable embedding prefix extractable from " << embedinto_filename; + } mPrimGen->embedInto(embedinto_filename); } @@ -197,6 +209,19 @@ class O2PrimaryServerDevice final : public fair::mq::Device auto& vertex = vertices.at(collisionindex); LOG(info) << "Setting vertex " << vertex << " for event " << mEventCounter << " for prefix " << mSimConfig.getOutPrefix() << " from CollContext"; mPrimGen->setExternalVertexForNextEvent(vertex.X(), vertex.Y(), vertex.Z()); + + // set correct embedding index for PrimaryGenerator ... based on collision context for embedding + auto& collisionParts = mCollissionContext->getEventParts()[collisionindex]; + int background_index = -1; // -1 means no embedding taking place for this signal + + // find the part that corresponds to the event embeded into + for (auto& part : collisionParts) { + if (mCollissionContext->getSimPrefixes()[part.sourceID] == mEmbeddIntoPrefix) { + background_index = part.entryID; + LOG(info) << "Setting embedding index to " << background_index; + } + } + mPrimGen->setEmbedIndex(background_index); } } mPrimGen->GenerateEvent(mStack); @@ -222,34 +247,52 @@ class O2PrimaryServerDevice final : public fair::mq::Device } } - // launches a thread that listens for status requests from outside asynchronously + // launches a thread that listens for status/config/shutdown requests from outside asynchronously void launchInfoThread() { static std::vector threads; + auto sendErrorReply = [](fair::mq::Channel& channel) { + LOG(error) << "UNKNOWN REQUEST"; + std::unique_ptr reply(channel.NewSimpleMessage((int)(404))); + channel.Send(reply); + }; + LOG(info) << "LAUNCHING STATUS THREAD"; - auto lambda = [this]() { - while (mState != O2PrimaryServerState::Stopped) { + auto lambda = [this, sendErrorReply]() { + bool canShutdown{false}; + // Exit only when both: serving stopped and allowed from outside. + while (!(mState == O2PrimaryServerState::Stopped && canShutdown)) { auto& channel = GetChannels().at("o2sim-primserv-info").at(0); if (!channel.IsValid()) { LOG(error) << "channel primserv-info not valid"; } - std::unique_ptr request(channel.NewSimpleMessage(-1)); + std::unique_ptr request(channel.NewSimpleMessage((int)(-1))); int timeout = 100; // 100ms --> so as not to block and allow for proper termination of this thread if (channel.Receive(request, timeout) > 0) { - LOG(info) << "INFO REQUEST RECEIVED"; - if (*(int*)(request->GetData()) == (int)O2PrimaryServerInfoRequest::Status) { + int request_payload; // we expect an (int) ~ to type O2PrimaryServerInfoRequest + if (request->GetSize() != sizeof(request_payload)) { + LOG(error) << "Obtained request with unexpected payload size"; + sendErrorReply(channel); // ALWAYS reply + } + + memcpy(&request_payload, request->GetData(), sizeof(request_payload)); + + if (request_payload == (int)O2PrimaryServerInfoRequest::Status) { LOG(info) << "Received status request"; // request needs to be a simple enum of type O2PrimaryServerInfoRequest std::unique_ptr reply(channel.NewSimpleMessage((int)mState.load())); if (channel.Send(reply) > 0) { LOG(info) << "Send status successful"; } - } else if (*(int*)request->GetData() == (int)O2PrimaryServerInfoRequest::Config) { + } else if (request_payload == (int)O2PrimaryServerInfoRequest::Config) { HandleConfigRequest(channel); + } else if (request_payload == (int)O2PrimaryServerInfoRequest::AllowShutdown) { + LOG(info) << "Got info that we may shutdown"; + std::unique_ptr ack(channel.NewSimpleMessage(200)); + channel.Send(ack); + canShutdown = true; } else { - LOG(fatal) << "UNKNOWN REQUEST"; - std::unique_ptr reply(channel.NewSimpleMessage(404)); - channel.Send(reply); + sendErrorReply(channel); } } } @@ -425,6 +468,8 @@ class O2PrimaryServerDevice final : public fair::mq::Device if (channel.Send(message) > 0) { LOG(info) << "config reply send "; return true; + } else { + LOG(error) << "Failure sending config reply "; } return true; } @@ -479,10 +524,13 @@ class O2PrimaryServerDevice final : public fair::mq::Device void PostRun() override { + // We shouldn't shut down immediately when all events have been served + // Instead we also need to wait until the info thread running some communication server + // with other processes is finished. while (!mInfoThreadStopped) { LOG(info) << "Waiting info thread"; using namespace std::chrono_literals; - std::this_thread::sleep_for(100ms); + std::this_thread::sleep_for(1000ms); } } @@ -495,7 +543,7 @@ class O2PrimaryServerDevice final : public fair::mq::Device if (mEventCounter >= mMaxEvents && mNeedNewEvent) { workavailable = false; } - if (!(mState == O2PrimaryServerState::ReadyToServe || mState == O2PrimaryServerState::WaitingEvent)) { + if (!(mState.load() == O2PrimaryServerState::ReadyToServe || mState.load() == O2PrimaryServerState::WaitingEvent)) { // send a zero answer workavailable = false; } @@ -696,6 +744,7 @@ class O2PrimaryServerDevice final : public fair::mq::Device // some information specific to use case when we have a collision context o2::steer::DigitizationContext* mCollissionContext = nullptr; //! std::unordered_map mEventID_to_CollID; //! + std::string mEmbeddIntoPrefix; //! sim prefix of background events TRandom3 mSeedGenerator; //! specific random generator for seed generation for work chunks }; diff --git a/run/O2SimDevice.h b/run/O2SimDevice.h index 07f7e9f82e249..9256734cce487 100644 --- a/run/O2SimDevice.h +++ b/run/O2SimDevice.h @@ -95,10 +95,7 @@ class O2SimDevice final : public fair::mq::Device // returns true if successful / false if not static bool querySimConfig(fair::mq::Channel& channel) { - // auto text = new std::string("configrequest"); - // std::unique_ptr request(channel.NewMessage(const_cast(text->c_str()), - // text->length(), CustomCleanup, text)); - std::unique_ptr request(channel.NewSimpleMessage(O2PrimaryServerInfoRequest::Config)); + std::unique_ptr request(channel.NewSimpleMessage((int)O2PrimaryServerInfoRequest::Config)); std::unique_ptr reply(channel.NewMessage()); int timeoutinMS = 60000; // wait for 60s max --> should be fast reply @@ -164,7 +161,7 @@ class O2SimDevice final : public fair::mq::Device while (reprobe) { reprobe = false; int i = -1; - fair::mq::MessagePtr request(statuschannel.NewSimpleMessage(O2PrimaryServerInfoRequest::Status)); + fair::mq::MessagePtr request(statuschannel.NewSimpleMessage((int)O2PrimaryServerInfoRequest::Status)); fair::mq::MessagePtr reply(statuschannel.NewSimpleMessage(i)); auto sendcode = statuschannel.Send(request, timeoutinMS); if (sendcode > 0) { diff --git a/run/PrimaryServerState.h b/run/PrimaryServerState.h index 5a15cca12b9b1..4bae1d566dc60 100644 --- a/run/PrimaryServerState.h +++ b/run/PrimaryServerState.h @@ -25,10 +25,11 @@ enum class O2PrimaryServerState { }; static const char* PrimStateToString[5] = {"INIT", "SERVING", "WAITEVENT", "IDLE", "STOPPED"}; -/// enum class for type of info request +/// enum class for request to o2sim-primserv-info channel of the O2PrimaryServerDevice enum class O2PrimaryServerInfoRequest { - Status = 1, - Config = 2 + Status = 1, // asks to retrieve current status of O2PrimaryServerDevice --> will send O2PrimaryServerState + Config = 2, // asks for o2-sim config reply + AllowShutdown = 3 // can be used to let particle server know that shutdown is now safe (once all components initialized) }; /// Struct to be used as payload when making a request diff --git a/run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybrid.ini b/run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybrid.ini new file mode 100644 index 0000000000000..0105349ea4d42 --- /dev/null +++ b/run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybrid.ini @@ -0,0 +1,3 @@ +[GeneratorHybrid] +configFile = ${O2_ROOT}/examples/ExternalToHybrid/sequential.json +switchExtToHybrid = true \ No newline at end of file diff --git a/run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybridCocktail.ini b/run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybridCocktail.ini new file mode 100644 index 0000000000000..11728f970d688 --- /dev/null +++ b/run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybridCocktail.ini @@ -0,0 +1,3 @@ +[GeneratorHybrid] +configFile = ${O2_ROOT}/examples/ExternalToHybrid/cocktail.json +switchExtToHybrid = true diff --git a/run/SimExamples/ExternalToHybrid/README.md b/run/SimExamples/ExternalToHybrid/README.md new file mode 100644 index 0000000000000..28292cdf9277a --- /dev/null +++ b/run/SimExamples/ExternalToHybrid/README.md @@ -0,0 +1,45 @@ + + +This example demonstrates how to bypass the Hyperloop limitations when using external generators by switching the configuration to hybrid mode, using the new `GeneratorHybrid.switchExtToHybrid` parameter (set to false by default). + +This solution works only with updated O2sim versions containing the `switchExtToHybrid` option. + +# Configuration Files + +Two example configuration files are provided, each pointing to different hybrid JSON files: + +- **GeneratorHyperloopHybridCocktail.ini** → Creates a cocktail mixing two Pythia8 based generators and a boxgen instance +- **GeneratorHyperloopHybrid.ini** → Defines sequential generation of boxgen and EPOS4 events called with an external generator + +# Script Description + +## rundpl.sh + +This script demonstrates event generation using the DPL framework, launching it with the external generator in hybrid mode. + +### Available Flags + +- **-i, --ini CONFIG** → Specifies the configuration ini file (default: `GeneratorHyperloopHybridCocktail.ini`) +- **-n, --nevents EVENTS** → Sets the number of events to generate (default: 5) +- **-h, --help** → Prints usage instructions and o2-sim-dpl-eventgen help +- **--** → Passes remaining command line arguments to o2-sim-dpl-eventgen + +### Usage Examples + +Run with default settings (5 events using cocktail configuration): +```bash +./rundpl.sh +``` + +Generate 10 events using the sequential configuration: +```bash +./rundpl.sh -n 10 -i ${O2_ROOT}/examples/ExternalToHybrid/GeneratorHyperloopHybrid.ini +``` + +# Requirements + +- O2sim version with `switchExtToHybrid` support +- O2_ROOT and O2DPG_MC_CONFIG_ROOT environment variable must be loaded (possibly via O2sim directly) +- Appropriate external generator configurations (e.g., EPOS4) must be available \ No newline at end of file diff --git a/run/SimExamples/ExternalToHybrid/cocktail.json b/run/SimExamples/ExternalToHybrid/cocktail.json new file mode 100644 index 0000000000000..2e8a4c964b1c6 --- /dev/null +++ b/run/SimExamples/ExternalToHybrid/cocktail.json @@ -0,0 +1,49 @@ +{ + "generators": [ + { + "cocktail": [ + { + "name": "pythia8", + "config": { + "config": "${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_13tev.ini" + } + }, + { + "name": "boxgen", + "config": { + "pdg": 443, + "number": 10, + "eta": [ + -0.8, + 0.8 + ], + "prange": [ + 0.1, + 5 + ], + "phirange": [ + 0, + 360 + ] + } + } + ] + } + ], + "fractions": [ + 1 + ] +} \ No newline at end of file diff --git a/run/SimExamples/ExternalToHybrid/rundpl.sh b/run/SimExamples/ExternalToHybrid/rundpl.sh new file mode 100755 index 0000000000000..e9bd15b239862 --- /dev/null +++ b/run/SimExamples/ExternalToHybrid/rundpl.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# This is a simple example script to bypass the Hyperloop limitations in using +# external generators only, by switching the generator to the hybrid mode + +# This script works only with updated O2sim version containing the switchExtToHybrid option + +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 2 +[ ! "${O2DPG_MC_CONFIG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 2 + +NEV=5 +# Two example ini configurations are provided pointing to different hybrid JSON files +# One creates a cocktail based on Pythia8, while the other generates sequentially EPOS4 and boxgen events +ini="${O2_ROOT}/examples/ExternalToHybrid/GeneratorHyperloopHybridCocktail.ini" + +usage() +{ + cat </dev/stderr + exit 3 + ;; + esac + shift +done + +# Starting the dpl-eventgen simulation +o2-sim-dpl-eventgen -b --generator external --nEvents $NEV --configFile $ini \ No newline at end of file diff --git a/run/SimExamples/ExternalToHybrid/sequential.json b/run/SimExamples/ExternalToHybrid/sequential.json new file mode 100644 index 0000000000000..bfb810f745f6a --- /dev/null +++ b/run/SimExamples/ExternalToHybrid/sequential.json @@ -0,0 +1,35 @@ +{ + "generators": [ + { + "name": "boxgen", + "config": { + "pdg": 443, + "number": 10, + "eta": [ + -0.8, + 0.8 + ], + "prange": [ + 0.1, + 5 + ], + "phirange": [ + 0, + 360 + ] + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/ini/GeneratorEPOS4_pp13TeV.ini" + } + } + ], + "fractions": [ + 1, + 1 + ] +} \ No newline at end of file diff --git a/run/SimExamples/HepMC_HERWIG7/LHC.in b/run/SimExamples/HepMC_HERWIG7/LHC.in new file mode 100644 index 0000000000000..ef3641bf7b174 --- /dev/null +++ b/run/SimExamples/HepMC_HERWIG7/LHC.in @@ -0,0 +1,49 @@ +# -*- ThePEG-repository -*- +################################################################################ +# This file contains our best tune to UE data from ATLAS at 7 TeV. More recent +# tunes and tunes for other centre-of-mass energies as well as more usage +# instructions can be obtained from this Herwig wiki page: +# http://projects.hepforge.org/herwig/trac/wiki/MB_UE_tunes +# The model for soft interactions and diffractions is explained in +# [S. Gieseke, P. Kirchgaesser, F. Loshaj, arXiv:1612.04701] +################################################################################ + +read snippets/PPCollider.in + +################################################## +# Technical parameters for this run +################################################## +cd /Herwig/Generators +################################################## +# LHC physics parameters (override defaults here) +################################################## +set EventGenerator:EventHandler:LuminosityFunction:Energy 13600.0 + +# Minimum Bias +read snippets/MB.in + +# Recommended set of parameters for MB/UE runs + +set /Herwig/Hadronization/ColourReconnector:ReconnectionProbability 0.5 +set /Herwig/UnderlyingEvent/MPIHandler:pTmin0 3.502683 +set /Herwig/UnderlyingEvent/MPIHandler:InvRadius 1.402055 +set /Herwig/UnderlyingEvent/MPIHandler:Power 0.416852 +set /Herwig/Partons/RemnantDecayer:ladderPower -0.08 +set /Herwig/Partons/RemnantDecayer:ladderNorm 0.95 + +################################################## +# Analyses +################################################## +## Hepmc file creation +create ThePEG::HepMCFile /Herwig/Analysis/HepMC HepMCAnalysis.so +set /Herwig/Analysis/HepMC:PrintEvent 10 +set /Herwig/Analysis/HepMC:Format GenEvent +set /Herwig/Analysis/HepMC:Units GeV_mm +set /Herwig/Analysis/HepMC:Filename herwig.hepmc +insert /Herwig/Generators/EventGenerator:AnalysisHandlers 0 /Herwig/Analysis/HepMC + + +################################################## +# Save run for later usage with 'Herwig run' +################################################## +saverun LHC EventGenerator \ No newline at end of file diff --git a/run/SimExamples/HepMC_HERWIG7/README.md b/run/SimExamples/HepMC_HERWIG7/README.md new file mode 100755 index 0000000000000..6366c1f076672 --- /dev/null +++ b/run/SimExamples/HepMC_HERWIG7/README.md @@ -0,0 +1,25 @@ + + +The usage of HERWIG7 with the O2 machinery is presented in this short manual. +The example generates HEPMC3 data using the Herwig executable and then +reads the data via the hepmc generator defined in o2-sim. + +# Files description + +Two files are provided in the folder: +- **runo2sim.sh** → allows the generation of events using o2-sim +- **LHC.in** → example input file for the configuration of the HERWIG generator + +## runo2sim.sh + +The script works after loading any O2sim version containing HERWIG7 as a package (dependence of AliGenO2). + +If no parameters are provided, the script will run with default values (energy and nEvents provided in the LHC.in file), but few flags are available to change the settings on-the-fly: +- **-m , --more** → feeds the simulation with advanced parameters provided to the configuration key flags +- **-n , --nevents** → changes the number of events in the .in file or gets the one in the file if no events are provided +- **-i , --input** → .in filename for HERWIG7 configuration +- **-j , --jobs** → sets the number of workers (2 jobs by default) +- **-e , --ecm** → sets the center-of-mass energy in the input file +- **-h , --help** → prints usage instructions \ No newline at end of file diff --git a/run/SimExamples/HepMC_HERWIG7/runo2sim.sh b/run/SimExamples/HepMC_HERWIG7/runo2sim.sh new file mode 100755 index 0000000000000..536a47de01d5c --- /dev/null +++ b/run/SimExamples/HepMC_HERWIG7/runo2sim.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# +# This is a simple simulation example on how to generate HEPMC3 data from +# HERWIG7 and run an ALICE simulation using the o2-sim executable +# In the script we assume that the .run file has the same name of the input file, so change it accordingly. +# This script works only with AliGenO2 version containing the HERWIG7 generator + +# HERWIG7 and O2 must be loaded +set -x +if [ ! "${HERWIG_ROOT}" ]; then + echo "This needs HERWIG7 loaded; alienv enter ..." + exit 1 +fi + +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 2 + +NEV=-1 +more="" +input="LHC" +eCM=-1 +JOBS=2 + +usage() +{ + cat </dev/stderr + exit 3 + ;; + esac + shift +done + +echo "Input file: $input" + +if [ ! -f $input.in ]; then + echo "Error: Input file $input.in not found" + exit 4 +else + if grep -Fq "saverun" $input.in; then + sed -i "/saverun/c\saverun $input EventGenerator" $input.in + else + echo "saverun $input EventGenerator" >> $input.in + fi +fi + +# Set number of events to write in HepMC in input file +if [ ! $NEV -eq -1 ]; then + echo "Setting number of events to $NEV" + if grep -Fq "PrintEvent" $input.in; then + sed -i "/PrintEvent/c\set /Herwig/Analysis/HepMC:PrintEvent $NEV" $input.in + else + echo "set /Herwig/Analysis/HepMC:PrintEvent $NEV" >> $input.in + fi +else + echo "Number of events not set, checking input file..." + if grep -Fq "PrintEvent" $input.in; then + NEV=$(grep -F "PrintEvent" $input.in | awk '{print $3}') + echo "Number of events set to $NEV" + else + echo "Error: Number of events not set in HERWIG7" + exit 5 + fi +fi + +# Set ECM + +if [ ! $eCM -eq -1 ]; then + echo "Setting eCM to $eCM" + if grep -Fq "Energy" $input.in; then + sed -i "/Energy/c\set EventGenerator:EventHandler:LuminosityFunction:Energy $eCM" $input.in + else + echo "set EventGenerator:EventHandler:LuminosityFunction:Energy $eCM" >> $input.in + fi +else + echo "Energy not set, checking input file..." + if grep -Fq "Energy" $input.in; then + eCM=$(grep -F "Energy" $input.in | awk '{print $3}') + echo "Energy set to $eCM" + else + echo "Error: eCM not set in HERWIG7" + exit 6 + fi +fi + +# Generating events using HERWIG7 +Herwig read --repo=${HERWIG_ROOT}/share/Herwig/HerwigDefaults.rpo $input.in +Herwig run -N $NEV $input.run + +# Starting simulation with o2-sim +o2-sim -j $JOBS -n ${NEV} -g hepmc \ + --configKeyValues "GeneratorFileOrCmd.fileNames=herwig.hepmc;${more}" diff --git a/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh b/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh index f08de81b92d8c..7671d03b97b8f 100755 --- a/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh +++ b/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh @@ -18,7 +18,7 @@ set -x # PART b) ... apply vertex smearing on top of HepMC events and perform simple analysis NEV=$(grep EVENT slight.out | wc -l) -o2-sim-dpl-eventgen -b --nevents ${NEV} --generator hepmc --confKeyValues \ +o2-sim-dpl-eventgen -b --nEvents ${NEV} --generator hepmc --configKeyValues \ "GeneratorFileOrCmd.fileNames=starlight.hepmc;Diamond.position[2]=0.1;Diamond.width[2]=0.05" |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b diff --git a/run/SimExamples/McTracksToAOD/run_O2Kine.sh b/run/SimExamples/McTracksToAOD/run_O2Kine.sh index 7506f00834fcf..9afac20cd1a0b 100755 --- a/run/SimExamples/McTracksToAOD/run_O2Kine.sh +++ b/run/SimExamples/McTracksToAOD/run_O2Kine.sh @@ -10,6 +10,7 @@ NEVENTS=1000 # launch generator process (for 10000 min bias Pythia8 events; no Geant; no geometry) # o2-sim -j 1 -g pythia8pp -n ${NEVENTS} --noGeant --vertexMode kNoVertex &> sim.log +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) # Option 1) -- use o2-mckine-publisher [ -f AnalysisResults.root ] && rm AnalysisResults.root o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe 10 |\ @@ -19,8 +20,8 @@ mv AnalysisResults.root AnalysisResult_1.root # Option 2) -- use o2-sim-dpl-eventgen + extkinO2 generator (this should be equivalent to Option 1) [ -f AnalysisResults.root ] && rm AnalysisResults.root -o2-sim-dpl-eventgen -b --nevents ${NEVENTS} --aggregate-timeframe 10 --generator extkinO2 \ - --confKeyValues "GeneratorFromO2Kine.fileName=o2sim_Kine.root" --vertexMode kNoVertex |\ +o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator extkinO2 \ + --configKeyValues "GeneratorFromO2Kine.fileName=o2sim_Kine.root" --vertexMode kNoVertex |\ o2-sim-mctracks-to-aod -b |\ o2-analysis-mctracks-to-aod-simple-task -b &> log2 mv AnalysisResults.root AnalysisResult_2.root diff --git a/run/SimExamples/McTracksToAOD/run_Pythia8.sh b/run/SimExamples/McTracksToAOD/run_Pythia8.sh index 8bac774c5892b..93d2024b05d37 100755 --- a/run/SimExamples/McTracksToAOD/run_Pythia8.sh +++ b/run/SimExamples/McTracksToAOD/run_Pythia8.sh @@ -8,7 +8,7 @@ NEVENTS=1000 # --aggregate-timeframe 10 is used to combine 10 generated events into a timeframe that is then converted to AOD tables # note that if you need special configuration for the analysis tasks, it needs to be passed to proxy and converter as well - +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --vertexMode kNoVertex |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b &> pythia8.log diff --git a/run/SimExamples/McTracksToAOD/run_trigger.sh b/run/SimExamples/McTracksToAOD/run_trigger.sh index ca720191cbad2..5b278c0b666e5 100755 --- a/run/SimExamples/McTracksToAOD/run_trigger.sh +++ b/run/SimExamples/McTracksToAOD/run_trigger.sh @@ -5,9 +5,9 @@ set -x NEVENTS=1000 - +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) CONFKEY="TriggerExternal.fileName=trigger.macro;TriggerExternal.funcName=trigger()" -o2-sim-dpl-eventgen -b --nevents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --trigger external \ - --vertexMode kDiamondParam --confKeyValues "${CONFKEY}" |\ +o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --trigger external \ + --vertexMode kDiamondParam --configKeyValues "${CONFKEY}" |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index f72dd6eebaaf0..cfbea6ae02a5f 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -13,7 +13,6 @@ #include "Framework/AnalysisTask.h" #include "Monitoring/Monitoring.h" #include "Framework/CommonDataProcessors.h" -#include "SimulationDataFormat/MCTrack.h" #include "Steer/MCKinematicsReader.h" #include "Framework/runDataProcessing.h" @@ -64,6 +63,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) spec.outputs.emplace_back("MC", "MCHEADER", 0, Lifetime::Timeframe); spec.outputs.emplace_back("MC", "MCTRACKS", 0, Lifetime::Timeframe); spec.requiredServices.push_back(o2::framework::ArrowSupport::arrowBackendSpec()); - spec.algorithm = CommonDataProcessors::wrapWithRateLimiting(spec.algorithm); + spec.algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(spec.algorithm); return {spec}; } diff --git a/run/o2simdefaultdetectorlist.json b/run/o2simdefaultdetectorlist.json index 697fafcba5872..2a7e977be741d 100644 --- a/run/o2simdefaultdetectorlist.json +++ b/run/o2simdefaultdetectorlist.json @@ -58,6 +58,7 @@ "RCH", "MI3", "ECL", + "FD3", "HALL", "MAG", "A3IP", diff --git a/scripts/geometry/O2_CADtoTGeo.py b/scripts/geometry/O2_CADtoTGeo.py new file mode 100644 index 0000000000000..d564cdc6124a8 --- /dev/null +++ b/scripts/geometry/O2_CADtoTGeo.py @@ -0,0 +1,602 @@ +#!/usr/bin/env python3 +""" +A Python script, doing a deep STEP/XCAF -> ROOT TGeo conversion. +For now, all CAD solids are simply meshed. The ROOT geometry is build as a C++ ROOT macro +and facet data is stored in binary form to keep disc space minimal. + +Generates (into --output-folder): + - geom.C (small ROOT macro) + - facets__.bin for each leaf logical volume (float32 triangles) + +Facet file format (little-endian): + uint32 nTriangles + then nTriangles * 9 * float32: + ax ay az bx by bz cx cy cz + +VOLNAME is a filename-safe version of the XCAF label name when available (e.g. "nut"), +and LID is the XCAF label entry (e.g. "0:1:1:7" -> "0_1_1_7") to keep filenames unique. + +Naming: + - C++ variable names stay based on XCAF label entry (e.g. 0:1:1:7) for uniqueness. + - ROOT object names (TGeoVolume / TGeoTessellated / TGeoVolumeAssembly) use the label's + human name when available (e.g. "nut", "rod-assembly"), falling back to the entry. + +Units: + - By default, the script tries to detect the STEP LENGTH unit by scanning the STEP file + header/contents (common patterns like .MILLI. / .CENTI. / .METRE. / INCH / FOOT). + - You can override with --step-unit {auto,mm,cm,m,in,ft}. TGeo expects cm. + +Author: + - Sandro Wenzel, CERN (02/2026) +""" + +import warnings +warnings.filterwarnings("ignore", message=".*all to deprecated function.*", category=DeprecationWarning) + +import argparse +import re +import struct +from pathlib import Path as _Path + +from OCC.Core.Bnd import Bnd_Box +from OCC.Core.BRepBndLib import brepbndlib +from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Core.BRep import BRep_Tool +from OCC.Core.TopLoc import TopLoc_Location +from OCC.Core.TopAbs import TopAbs_REVERSED +from OCC.Extend.TopologyUtils import TopologyExplorer + +from OCC.Core.STEPCAFControl import STEPCAFControl_Reader +from OCC.Core.TDocStd import TDocStd_Document +from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool +from OCC.Core.IFSelect import IFSelect_RetDone + +from OCC.Core.TDF import TDF_Label, TDF_LabelSequence, TDF_Tool +from OCC.Core.TCollection import TCollection_AsciiString +from OCC.Core.gp import gp_Trsf + + +# ------------------------------- +# STEP/XCAF loading +# ------------------------------- + +def load_step_with_xcaf(path: str): + doc = TDocStd_Document("pythonocc-doc") + reader = STEPCAFControl_Reader() + reader.SetColorMode(True) + reader.SetNameMode(True) + reader.SetLayerMode(True) + + status = reader.ReadFile(path) + if status != IFSelect_RetDone: + raise RuntimeError(f"STEP read failed for: {path}") + + reader.Transfer(doc) + shape_tool = XCAFDoc_DocumentTool.ShapeTool(doc.Main()) + return doc, shape_tool + + +def label_id(label: TDF_Label) -> str: + s = TCollection_AsciiString() + TDF_Tool.Entry(label, s) + return s.ToCString() + + +def label_name(label: TDF_Label) -> str: + # Uses the XCAF/STEP name when present; can be empty. + try: + n = label.GetLabelName() + if n: + return str(n) + except Exception: + pass + return "" + + +# ------------------------------- +# Units +# ------------------------------- + +def step_unit_scale_to_cm(step_unit: str) -> float: + step_unit = (step_unit or "auto").lower() + if step_unit == "mm": + return 0.1 + if step_unit == "cm": + return 1.0 + if step_unit == "m": + return 100.0 + if step_unit == "in": + return 2.54 + if step_unit == "ft": + return 30.48 + raise ValueError(f"Unknown --step-unit {step_unit} (use auto, mm, cm, m, in, ft)") + + +def detect_step_length_unit(step_path: str) -> str: + """ + Heuristic unit detection by scanning STEP file text for common unit tokens. + This avoids relying on OCCT APIs that can vary across pythonOCC builds. + + Returns one of: mm, cm, m, in, ft. Defaults to mm if uncertain. + """ + p = _Path(step_path) + # STEP can be huge: read only the first few MB; units are near the header. + max_bytes = 4 * 1024 * 1024 + data = p.open("rb").read(max_bytes).decode("latin-1", errors="ignore").upper() + + if ".MILLI." in data: + return "mm" + if ".CENTI." in data: + return "cm" + if ".METRE." in data or ".METER." in data: + return "m" + if "INCH" in data: + return "in" + if "FOOT" in data or "FEET" in data: + return "ft" + + # Conservative default for mechanical CAD STEP is mm + return "mm" + + +# ------------------------------- +# Triangulation helpers +# ------------------------------- + +def _scale_triangles(triangles, s: float): + if s == 1.0: + return triangles + out = [] + for (a, b, c) in triangles: + out.append(( + (a[0] * s, a[1] * s, a[2] * s), + (b[0] * s, b[1] * s, b[2] * s), + (c[0] * s, c[1] * s, c[2] * s), + )) + return out + + +def triangulate_asbbox(shape, scale_to_cm: float = 1.0): + box = Bnd_Box() + brepbndlib.Add(shape, box) + xmin, ymin, zmin, xmax, ymax, zmax = box.Get() + + p000 = (xmin, ymin, zmin) + p001 = (xmin, ymin, zmax) + p010 = (xmin, ymax, zmin) + p011 = (xmin, ymax, zmax) + p100 = (xmax, ymin, zmin) + p101 = (xmax, ymin, zmax) + p110 = (xmax, ymax, zmin) + p111 = (xmax, ymax, zmax) + + triangles = [ + (p000, p100, p110), (p000, p110, p010), + (p001, p111, p101), (p001, p011, p111), + (p000, p101, p100), (p000, p001, p101), + (p010, p110, p111), (p010, p111, p011), + (p000, p010, p011), (p000, p011, p001), + (p100, p101, p111), (p100, p111, p110), + ] + return _scale_triangles(triangles, scale_to_cm) + + +def triangulate_CAD_solid(my_solid, meshparam, scale_to_cm: float = 1.0): + lin_defl = float(meshparam.get("lin_defl", 0.1)) + ang_defl = float(meshparam.get("ang_defl", 0.1)) + + parallel = True + try: + BRepMesh_IncrementalMesh(my_solid, lin_defl, False, ang_defl, bool(parallel)) + except TypeError: + BRepMesh_IncrementalMesh(my_solid, lin_defl, False, ang_defl) + + triangles = [] + for face in TopologyExplorer(my_solid).faces(): + loc = TopLoc_Location() + triangulation = BRep_Tool.Triangulation(face, loc) + if triangulation is None: + continue + + trsf = loc.Transformation() + reverse = (face.Orientation() == TopAbs_REVERSED) + + for i in range(1, triangulation.NbTriangles() + 1): + tri = triangulation.Triangle(i) + n1, n2, n3 = tri.Get() + + p1 = triangulation.Node(n1).Transformed(trsf) + p2 = triangulation.Node(n2).Transformed(trsf) + p3 = triangulation.Node(n3).Transformed(trsf) + + if reverse: + p2, p3 = p3, p2 + + triangles.append(( + (p1.X(), p1.Y(), p1.Z()), + (p2.X(), p2.Y(), p2.Z()), + (p3.X(), p3.Y(), p3.Z()), + )) + + return _scale_triangles(triangles, scale_to_cm) + + +# ------------------------------- +# Naming helpers +# ------------------------------- + +def sanitize_cpp_name(s: str) -> str: + safe = re.sub(r"[^0-9a-zA-Z]", "_", s) + if not safe: + safe = "x" + if not (safe[0].isalpha() or safe[0] == "_"): + safe = "_" + safe + return safe + + +def sanitize_filename(s: str) -> str: + safe = re.sub(r"[^0-9a-zA-Z]", "_", s) + return safe or "x" + + +# ------------------------------- +# Binary facet IO +# ------------------------------- + +def write_facets_bin(path: _Path, triangles): + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "wb") as f: + f.write(struct.pack(" str: + m = trsf.GetRotation().GetMatrix() + t = trsf.TranslationPart() + return f""" + Double_t {name}_m[9] = {{ + {m.Value(1,1)}, {m.Value(1,2)}, {m.Value(1,3)}, + {m.Value(2,1)}, {m.Value(2,2)}, {m.Value(2,3)}, + {m.Value(3,1)}, {m.Value(3,2)}, {m.Value(3,3)} + }}; + TGeoRotation *{name}_rot = new TGeoRotation(); + {name}_rot->SetMatrix({name}_m); + TGeoCombiTrans *{name} = new TGeoCombiTrans({t.X()*scale_to_cm}, {t.Y()*scale_to_cm}, {t.Z()*scale_to_cm}, {name}_rot); +""" + + +def emit_cpp_prelude() -> str: + return """#include +#include +#include +#include +#include +#include + +static void LoadFacets(const std::string& file, TGeoTessellated* solid, bool check=false) +{ + std::ifstream in(file, std::ios::binary); + if (!in) throw std::runtime_error("Cannot open facet file: " + file); + + uint32_t nTri = 0; + in.read(reinterpret_cast(&nTri), sizeof(nTri)); + if (!in) throw std::runtime_error("Bad facet header in: " + file); + + for (uint32_t i=0;i(v), sizeof(v)); + if (!in) throw std::runtime_error("Unexpected EOF in: " + file); + + solid->AddFacet(TGeoTessellated::Vertex_t(v[0],v[1],v[2]), + TGeoTessellated::Vertex_t(v[3],v[4],v[5]), + TGeoTessellated::Vertex_t(v[6],v[7],v[8])); + } + solid->CloseShape(check, true); +} +""" + + +def emit_materials_cpp() -> str: + return """ // Default material/medium (placeholder; can be replaced later) + TGeoMaterial *mat_Default = new TGeoMaterial("Default", 0., 0., 0.); + TGeoMedium *med_Default = new TGeoMedium("Default", 1, mat_Default); +""" + + +def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int) -> str: + safe = sanitize_cpp_name(lid) + shape_name = vol_display_name if vol_display_name else lid + + if ntriangles <= 0: + out = [] + out.append(f' TGeoBBox *solid_{safe} = new TGeoBBox("{shape_name}", 0.001, 0.001, 0.001);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + return "\n".join(out) + + out = [] + out.append(f' TGeoTessellated *solid_{safe} = new TGeoTessellated("{shape_name}", {ntriangles});') + out.append(f' LoadFacets("{facet_abspath}", solid_{safe}, check);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + return "\n".join(out) + + +def emit_assembly_cpp(lid: str, asm_display_name: str) -> str: + safe = sanitize_cpp_name(lid) + name = asm_display_name if asm_display_name else lid + return f' TGeoVolumeAssembly *asm_{safe} = new TGeoVolumeAssembly("{name}");' + + +# ------------------------------- +# Definition graph extraction +# ------------------------------- + +logical_volumes = {} # def_lid -> triangles +def_names = {} # def_lid -> human display name (may be "") +assemblies = set() # def_lid +placements = [] # (parent_def_lid, child_def_lid, gp_Trsf local) +top_defs = set() # top definition lids +visited_defs = set() # expanded defs + + +def cpp_var_for_def(lid: str) -> str: + safe = sanitize_cpp_name(lid) + return f"asm_{safe}" if lid in assemblies else f"vol_{safe}" + + +def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to_cm: float = 1.0): + def_lid = label_id(def_label) + if def_lid in visited_defs: + return + visited_defs.add(def_lid) + + nm = label_name(def_label) + if nm and def_lid not in def_names: + def_names[def_lid] = nm + elif def_lid not in def_names: + def_names[def_lid] = "" + + children = TDF_LabelSequence() + shape_tool.GetComponents(def_label, children) + has_children = children.Length() > 0 + + if has_children or shape_tool.IsAssembly(def_label): + assemblies.add(def_lid) + + for i in range(children.Length()): + child = children.Value(i + 1) + if shape_tool.IsReference(child): + referred = TDF_Label() + shape_tool.GetReferredShape(child, referred) + child_def_lid = label_id(referred) + + loc = shape_tool.GetLocation(child) + trsf = loc.Transformation() + placements.append((def_lid, child_def_lid, trsf)) + + expand_definition(referred, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + else: + child_def_lid = label_id(child) + placements.append((def_lid, child_def_lid, gp_Trsf())) + expand_definition(child, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + return + + if shape_tool.IsSimpleShape(def_label): + if def_lid not in logical_volumes: + shape = shape_tool.GetShape(def_label) + do_meshing = (meshparam is not None) and meshparam.get("do_meshing", None) is True + logical_volumes[def_lid] = triangulate_CAD_solid(shape, meshparam=meshparam, scale_to_cm=scale_to_cm) if do_meshing else triangulate_asbbox(shape, scale_to_cm=scale_to_cm) + return + + assemblies.add(def_lid) + + +def extract_graph(step_path: str, meshparam=None, scale_to_cm: float = 1.0): + global logical_volumes, def_names, assemblies, placements, top_defs, visited_defs + logical_volumes = {} + def_names = {} + assemblies = set() + placements = [] + top_defs = set() + visited_defs = set() + + doc, shape_tool = load_step_with_xcaf(step_path) + + roots = TDF_LabelSequence() + shape_tool.GetFreeShapes(roots) + + for i in range(roots.Length()): + root = roots.Value(i + 1) + if shape_tool.IsReference(root): + ref = TDF_Label() + shape_tool.GetReferredShape(root, ref) + top_defs.add(label_id(ref)) + expand_definition(ref, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + else: + top_defs.add(label_id(root)) + expand_definition(root, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + + return doc, shape_tool + + +# ------------------------------- +# ROOT macro emission +# ------------------------------- + +def emit_placement_cpp(parent_def: str, child_def: str, trsf: gp_Trsf, copy_no: int, scale_to_cm: float) -> str: + parent_cpp = cpp_var_for_def(parent_def) + child_cpp = cpp_var_for_def(child_def) + tr_name = f"tr_{sanitize_cpp_name(parent_def)}_{sanitize_cpp_name(child_def)}_{copy_no}" + return trsf_to_tgeo(trsf, tr_name, scale_to_cm) + f" {parent_cpp}->AddNode({child_cpp}, {copy_no}, {tr_name});\n" + + +def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit: str = "auto"): + if (step_unit or "auto").lower() == "auto": + detected = detect_step_length_unit(step_path) + scale_to_cm = step_unit_scale_to_cm(detected) + print(f"Detected STEP length unit: {detected} (scale to cm = {scale_to_cm})") + else: + scale_to_cm = step_unit_scale_to_cm(step_unit) + print(f"Using overridden STEP length unit: {step_unit} (scale to cm = {scale_to_cm})") + + extract_graph(step_path, meshparam=meshparam, scale_to_cm=scale_to_cm) + + out_folder = out_folder.expanduser().resolve() + out_folder.mkdir(parents=True, exist_ok=True) + + facet_files = {} # def_lid -> absolute path string + for lid, tris in logical_volumes.items(): + disp = def_names.get(lid, "") + volname = sanitize_filename(disp) if disp else "vol" + lidname = sanitize_filename(lid) + fname = f"facets_{volname}_{lidname}.bin" + fpath = (out_folder / fname).resolve() + write_facets_bin(fpath, tris) + facet_files[lid] = str(fpath).replace("\\", "\\\\") # C++ string literal safety + + cpp = [] + cpp.append(emit_cpp_prelude()) + + cpp.append("TGeoVolume* build(bool check=true) {") + cpp.append(' if (!gGeoManager) { throw std::runtime_error("gGeoManager is null. Call build_and_export() or create a TGeoManager first."); }') + cpp.append(emit_materials_cpp()) + + for lid in logical_volumes.keys(): + ntriangles = len(logical_volumes[lid]) + cpp.append(emit_tessellated_cpp(lid, def_names.get(lid, ""), facet_files[lid], ntriangles)) + + for lid in sorted(assemblies): + cpp.append(emit_assembly_cpp(lid, def_names.get(lid, ""))) + + for idx, (parent, child, trsf) in enumerate(placements, start=1): + cpp.append(emit_placement_cpp(parent, child, trsf, idx, scale_to_cm)) + + if len(top_defs) == 1: + top = next(iter(top_defs)) + cpp.append(f" return {cpp_var_for_def(top)};") + else: + cpp.append(' TGeoVolumeAssembly *asm_WORLD = new TGeoVolumeAssembly("WORLD");') + for i, node in enumerate(sorted(top_defs), start=1): + cpp.append(f" asm_WORLD->AddNode({cpp_var_for_def(node)}, {i});") + cpp.append(" return asm_WORLD;") + + cpp.append("}") + + # exports a function allowing to export the geometry to TGeo file + cpp.append('void build_and_export(const char* out_root = "geom.root", bool check=true) {') + cpp.append(' if (!gGeoManager) { new TGeoManager("geom","geom"); }') + cpp.append(' TGeoVolume* top = build(check);') + cpp.append(' gGeoManager->SetTopVolume(top);') + cpp.append(' gGeoManager->CloseGeometry();') + cpp.append(' gGeoManager->CheckOverlaps();') + cpp.append(' gGeoManager->Export(out_root);') + cpp.append('}') + + # exports a function to get get hold of the builder function in ALICE O2 + cpp.append('std::function get_builder_hook_checked() {') + cpp.append(' return []() { return build(true); };') + cpp.append('}') + # exports a function to get get hold of the builder function in ALICE O2 + cpp.append('std::function get_builder_hook_unchecked() {') + cpp.append(' return []() { return build(false); };') + cpp.append('}') + + return "\n".join(cpp) + + +# ------------------------------- +# Geometry Tree printing (debug) +# ------------------------------- + +def label_entry(label): + s = TCollection_AsciiString() + TDF_Tool.Entry(label, s) + return s.ToCString() + + +def traverse_print(label, shape_tool, depth=0): + indent = " " * depth + name = label.GetLabelName() + entry = label_entry(label) + print(f"{indent}- {name} =>[{entry}]") + + if shape_tool.IsReference(label): + ref_label = TDF_Label() + shape_tool.GetReferredShape(label, ref_label) + traverse_print(ref_label, shape_tool, depth + 1) + return + + children = TDF_LabelSequence() + shape_tool.GetComponents(label, children) + if children.Length() > 0 or shape_tool.IsAssembly(label): + for i in range(children.Length()): + traverse_print(children.Value(i + 1), shape_tool, depth + 1) + return + + if shape_tool.IsSimpleShape(label): + shape = shape_tool.GetShape(label) + print(f"{indent} [LogicalShape id={id(shape)}]") + + +def print_geom(step_file): + print(f"Printing GEOM hierarchy for {step_file}") + doc, shape_tool = load_step_with_xcaf(step_file) + roots = TDF_LabelSequence() + shape_tool.GetFreeShapes(roots) + for i in range(roots.Length()): + traverse_print(roots.Value(i + 1), shape_tool) + + +# ------------------------------- +# CLI +# ------------------------------- + +def main(): + ap = argparse.ArgumentParser(description="Convert STEP/XCAF to ROOT TGeo macro, facets in per-volume binary files.") + ap.add_argument("step", help="Input STEP file") + ap.add_argument("-o", "--out", default="geom.C", help="Output ROOT macro file name (default: geom.C)") + ap.add_argument("--output-folder", default="./", help="Output folder for macro + facet files") + ap.add_argument("--out-path", default=None, help="(deprecated) Alias for --output-folder") + ap.add_argument("--mesh", action="store_true", help="Use full BRepMesh triangulation instead of bounding boxes") + ap.add_argument("--print-tree", action="store_true", help="Just prints the geometry tree") + ap.add_argument("--mesh-prec", default=0.1, help="meshing precision. lower --> slower") + ap.add_argument("--step-unit", default="auto", choices=["auto", "mm", "cm", "m", "in", "ft"], help="STEP length unit override (default: auto-detect)") + + args = ap.parse_args() + + step_path = str(_Path(args.step).expanduser().resolve()) + if args.print_tree: + print_geom(step_path) + return + + out_folder = _Path(args.output_folder) + if args.out_path is not None: + out_folder = _Path(args.out_path) + + meshparam = {"do_meshing": args.mesh, "lin_defl": args.mesh_prec, "ang_defl": args.mesh_prec} + + out_folder = out_folder.expanduser().resolve() + out_folder.mkdir(parents=True, exist_ok=True) + + out_macro = (out_folder / _Path(args.out).name).resolve() + code = emit_root_macro(step_path, out_folder, meshparam=meshparam, step_unit=args.step_unit) + out_macro.write_text(code) + + print(f"Wrote ROOT macro: {out_macro}") + print(f"Wrote facet files into: {out_folder}") + print("In ROOT you can do:") + print(f" root -l {out_macro}") + print(' build_and_export("geom.root");') + + +if __name__ == "__main__": + main() diff --git a/scripts/geometry/README.md b/scripts/geometry/README.md new file mode 100644 index 0000000000000..4fb2d1ec610d4 --- /dev/null +++ b/scripts/geometry/README.md @@ -0,0 +1,27 @@ +This is the tool O2_CADtoTGeo.py which translates from geometries in STEP format (CAD export) to +TGeo. + +To use the tool, setup a conda environment with python-occ core installed. +The following should work on standard linux x86: + +``` +# -) download miniconda into $HOME/miniconda (if not already done) +if [ ! -d $HOME/miniconda ]; then + curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh -o miniconda.sh + bash miniconda.sh -b -p $HOME/miniconda +fi + +# -) source conda into the environment (in every shell you want to use this) +source $HOME/miniconda/etc/profile.d/conda.sh + +# -) Create an OCC environment (for OpenCacade) +conda create -n occ python=3.10 -y +conda activate occ + +# 3) Install OpenCascade Python bindings +conda install -c conda-forge pythonocc-core -y + +# 4) Run the tool, e.g. +conda activate occ +python PATH_TO_ALICEO2_SOURCES/scripts/geometry/O2_CADtoTGeo.py --help +``` \ No newline at end of file diff --git a/scripts/geometry/simulating_CAD_modules.md b/scripts/geometry/simulating_CAD_modules.md new file mode 100644 index 0000000000000..ccd59a3523781 --- /dev/null +++ b/scripts/geometry/simulating_CAD_modules.md @@ -0,0 +1,72 @@ +# ALICE-O2 GEANT Simulation of CAD Geometries + +These are a few notes related to the inclusion of external (CAD-described) detector modules into the O2 simulation framework. + +## Description of the Workflow + +In principle, such integration is now possible and requires the following steps: + +1. The CAD geometry needs to be exported to STEP format and must contain only the final geometry (no artificial eta-cut elements). Ideally, the geometry should be fully hierarchical with proper solid reuse. The solids should retain their proper surface representation for detailed analysis. + +2. A tool `O2-CADtoTGeo.py` is provided to convert the STEP geometry into TGeo format. The tool is part of AliceO2 and is based on Python bindings (OCC) for OpenCascade. The tool can be used as follows: + + ```bash + python O2-CADtoTGeo.py STEP_FILE --output-folder my_detector -o geom.C --mesh \ + --mesh-prec 0.2 + ``` + + This will create a ROOT macro file `geom.C` containing the geometry description in ROOT format, as well as several binary files describing the TGeo solids. The `geom.C` file can either be used directly in ROOT to inspect the geometry or be provided to ALICE-O2 for inclusion in the geometry. + +3. Introduction of materials/media in the file `geom.C`. Currently, the file `geom.C` needs to be patched or edited to properly include `TGeoMaterial`/`TGeoMedium` definitions and connect them to the relevant `TGeoVolume` objects. At present, every solid has the same dummy material attached, which is not realistic. It may be a good idea to create a new file `geom_withMaterials.C`, which differs from `geom.C` by the addition of these material definitions. + +4. Once the conversion is complete, the module can be inserted into the O2 geometry via the `ExternalModule` class. To do so, follow this pattern in `build_geometry.C`: + + ```cpp + if (isActivated("EXT")) { + o2::passive::ExternalModuleOptions options; + options.root_macro_file = "PATH_TO_MY_DETECTOR/my_detector/geom_withMaterials.C"; + options.anchor_volume = "barrel"; // hook this into barrel + auto rot = new TGeoCombiTrans(); + rot->RotateX(90); + rot->SetDy(30); // compensate for a shift of the barrel with respect to zero + options.placement = rot; + run->AddModule(new o2::passive::ExternalModule("A3VTX", "ALICE3 beam pipe", options)); + } + ``` + +5. Create a custom detector geometry list file `my_det.json` in JSON format that includes the external detector (and any other required components, such as the L3 magnet in this example): + + ```json + { + "MY_DET": [ + "EXT", + "MAG" + ] + } + ``` + +6. Run the Geant simulation with: + + ```bash + o2-sim --detectorList MY_DET:my_det.json -g pythia8pp .... + ``` + +## Known Limitations + +- The `O2-CADtoTGeo.py` tool currently converts geometries only into TGeoTessellated solids. This may be suboptimal for primitive shapes or only an approximation for shapes with exact second-order surfaces (e.g., tubes). The precision (and therefore the number of surface triangles) can be controlled with the `--mesh-prec` parameter. The smaller the value, the more precise the mesh. + +- Meshed solids created by the tool may have issues, such as topological errors or non-watertight surfaces. It is planned to include "healing" steps via additional processing with well-known geometry kernels (e.g., CGAL). + +- The tool does not currently export materials or TGeoMedia. These must be inserted or edited manually. It is planned to make this process more automatic and user-friendly. + +- The Python tool requires the OCC Python module, which is currently not part of our software distribution. We have found it most practical to run the tool in a separate conda environment (fully decoupled from the ALICE software stack). + +- The tool currently generates a `geom.C` macro file. In the future, it may be possible to directly create an in-memory TGeo representation for deeper integration. + +- Currently, only passive modules can be integrated. Treatment of sensitive volumes or parts will be addressed in a future step. + +## Software Installation + +- The simulation must be run in the standard O2 environment built with alibuild. + +- The CAD conversion tool must currently be run in a dedicated conda environment, as described in scripts/geometry/README.md in the AliceO2 source code. \ No newline at end of file